Module: Hosttag

Defined in:
lib/hosttag.rb,
lib/hosttag/server.rb

Defined Under Namespace

Classes: Server

Instance Method Summary collapse

Instance Method Details

#hosttag_add_tags(hosts, tags, options) ⇒ Object

Add the given tags to all the given hosts



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/hosttag.rb', line 101

def hosttag_add_tags(hosts, tags, options)
  r = hosttag_server(options)

  # Add tags to each host
  skip_host = {}
  all_hosts_skip_hosts = true
  hosts.each do |host|
    key = r.get_key('host', host)
    tags.each { |tag| r.sadd(key, tag) }

    if r.sismember(key, 'SKIP')
      skip_host[host] = true
    else
      all_hosts_skip_hosts = false
    end

    # Add to all_hosts sets
    all_hosts = r.get_key('all_hosts')
    all_hosts_full = r.get_key('all_hosts_full')
    # all_hosts shouldn't include SKIP hosts, so those we remove
    if skip_host[host]
      r.srem(all_hosts, host)
    else
      r.sadd(all_hosts, host)
    end
    r.sadd(all_hosts_full, host)
  end

  # Add hosts to each tag
  recheck_for_skip = false
  tags.each do |tag|
    # If we've added a SKIP tag to these hosts, flag to do some extra work
    recheck_for_skip = true if tag == 'SKIP'

    key = r.get_key('tag', tag)
    hosts.each do |host|
      # The standard case is to add the host to the list for this tag.
      # But we don't want SKIP hosts being included in these lists, so
      # for them we actually do a remove to make sure they're omitted.
      if skip_host[host] and tag != 'SKIP'
        r.srem(key, host)
      else
        r.sadd(key, host)
      end
    end

    # Add to all_tags sets
    all_tags = r.get_key('all_tags')
    all_tags_full = r.get_key('all_tags_full')
    r.sadd(all_tags, tag) unless all_hosts_skip_hosts
    r.sadd(all_tags_full, tag)
  end

  # If we've added a SKIP tag here, we need to recheck all tags for all skip hosts
  recheck_skip_change_for_all_tags(skip_host.keys, :add, r) if recheck_for_skip
end

#hosttag_all_hosts(options) ⇒ Object

Return an array of all hosts The final argument may be an options hash, which accepts the following keys:

  • :include_skip? - flag indicating whether to include hosts that have the SKIP tag set. Default: false i.e. omit hosts tagged with SKIP.



81
82
83
84
85
86
# File 'lib/hosttag.rb', line 81

def hosttag_all_hosts(options)
  r = hosttag_server(options)
  key = r.get_key(options[:include_skip?] ? 'all_hosts_full' : 'all_hosts')
  $stderr.puts "+ key: #{key}" if options[:debug]
  return r.smembers(key).sort
end

#hosttag_all_tags(options) ⇒ Object

Return an array of all tags The final argument may be an options hash, which accepts the following keys:

  • :include_skip? - flag indicating whether to include the SKIP tag. Default: false. Included for completeness.



93
94
95
96
97
98
# File 'lib/hosttag.rb', line 93

def hosttag_all_tags(options)
  r = hosttag_server(options)
  key = r.get_key(options[:include_skip?] ? 'all_tags_full' : 'all_tags')
  $stderr.puts "+ key: #{key}" if options[:debug]
  return r.smembers(key).sort
end

#hosttag_delete_all_tags(hosts, options) ⇒ Object

Delete all tags from the given hosts. Interactively confirms the deletions unless the :autoconfirm option is set. The final argument may be an options hash, which accepts the following keys:

  • :autoconfirm - if true, do deletes without asking for any confirmation



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/hosttag.rb', line 244

def hosttag_delete_all_tags(hosts, options)
  if not options[:autoconfirm]
    host_str = hosts.join(' ')
    print "Do you want to delete all tags on the following host(s):\n  #{host_str}\nConfirm? [yN] "
    $stdout.flush
    confirm = $stdin.gets.chomp
    return unless confirm =~ %r{^y}i
  end

  hosts.each do |host|
    begin
      tags = hosttag_lookup_hosts(host, options)
      hosttag_delete_tags([ host ], tags, options)
    rescue
      warn "Warning: invalid host '#{host}' - cannot delete"
    end
  end
end

#hosttag_delete_tags(hosts, tags, options) ⇒ Object

Delete the given tags from all the given hosts



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/hosttag.rb', line 159

def hosttag_delete_tags(hosts, tags, options)
  r = hosttag_server(options)

  # Delete tags from each host
  non_skip_host = {}
  hosts.each do |host|
    key = r.get_key('host', host)
    tags.each { |tag| r.srem(key, tag) }

    if r.sismember(key, 'SKIP')
      skip_host = true
    else
      non_skip_host[host] = true
    end

    # Delete from all_hosts sets
    all_hosts = r.get_key('all_hosts')
    all_hosts_full = r.get_key('all_hosts_full')
    # If all tags have been deleted, or this is a SKIP host, remove from all_hosts
    if r.scard(key) == 0 or skip_host
      r.srem(all_hosts, host)
    else
      # NB: we explicitly add here in case we've deleted a SKIP tag
      r.sadd(all_hosts, host)
    end
    if r.scard(key) == 0
      r.srem(all_hosts_full, host)
      r.del(key)
    end
  end

  # Delete hosts from each tag
  recheck_for_skip = false
  all_tags = r.get_key('all_tags')
  all_tags_full = r.get_key('all_tags_full')
  tags.each do |tag|
    # If we've deleted a SKIP tag from these hosts, flag to do some extra work
    recheck_for_skip = true if tag == 'SKIP'

    tag_key = r.get_key('tag', tag)
    hosts.each { |host| r.srem(tag_key, host) }

    # Delete from all_tags sets
    # If all hosts have been deleted (or this is the SKIP tag), remove from all_tags
    if r.scard(tag_key) == 0 or tag == 'SKIP'
      r.srem(all_tags, tag)
    else
      # NB: we explicitly add here in case we've deleted a SKIP tag
      r.sadd(all_tags, tag)
    end
    if r.scard(tag_key) == 0
      r.srem(all_tags_full, tag)
      r.del(tag_key)
    end
  end
  r.del(all_tags) if r.scard(all_tags) == 0
  r.del(all_tags_full) if r.scard(all_tags_full) == 0

  # If we've deleted a SKIP tag here, we need to recheck all tags for all non-skip hosts
  recheck_skip_change_for_all_tags(non_skip_host.keys, :delete, r) if recheck_for_skip
end

#hosttag_import_from_directory(datadir, options) ⇒ Object

Import hosts and tags from the given directory. The directory is expected to contain a set of directories, representing hosts; each file within those directories is treated as a tag that applies to that host. Options is a hash which accepts the following keys:

  • :delete - if true, delete ALL hosts and tags from the datastore before doing the import.

  • :autoconfirm - if true, don’t interactively confirm deletions



271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/hosttag.rb', line 271

def hosttag_import_from_directory(datadir, options)
  # Delete ALL hosts and tags from the datastore if options[:delete] set
  hosttag_truncate(options) if options[:delete]

  # Load directory into a { host => [ taglist ] } hash
  host_tag_hash = load_directory(datadir, options)

  # Add all hosts and tags
  host_tag_hash.each do |host, tags|
    hosttag_add_tags([ host ], tags, options)
  end
end

#hosttag_lookup(*args) ⇒ Object

Lookup the given host(s) or tag(s), returning an array of tags or hosts, as appropriate. If a :type option is not explicitly given, first tries the lookup using hosttag_lookup_tags, and if that fails retries using hosttag_lookup_hosts. The final argument may be an options hash, which accepts the following keys:

  • :type - either :host or :tag, specifying how to interpret the given arguments: :type => :host specifies that the arguments are hosts, and that the resultset should be a list of tags; :type => :tag specifies that the arguments are tags, and the resultset should be a list of hosts. Required, no default.

  • :rel - either :and or :or, specifying the relationship to use when interpreting the set of results. :rel => :and returns only results that have ALL of the given attributes i.e. the AND result set; :rel => :or returns results that have ANY of the given attributes i.e. the OR result set. Default: depends on :type - :and for :type

    > :host, and :or for :type => :tag.

  • :include_skip? - flag indicating whether to include hosts that have the SKIP tag set. Default: false i.e. omit hosts tagged with SKIP.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/hosttag.rb', line 60

def hosttag_lookup(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  return lookup_keys(args, options) if options[:type]

  begin
    return hosttag_lookup_tags(args, options)
  rescue => e
    begin
      return hosttag_lookup_hosts(args, options)
    rescue
      # If both lookups failed, re-raise original error
      raise e
    end
  end
end

#hosttag_lookup_hosts(*args) ⇒ Object

Lookup the given host(s), returning an array of tags that apply to them. If multiple hosts are given, by default the list of tags is those applying to ANY of the given hosts i.e. the results are ORed or unioned. To change this pass an explicit :rel => :and in the options hash. The final argument may be an options hash, which accepts the following keys:

  • :rel - either :and or :or, specifying the relationship to use when interpreting the set of hosts. :rel => :and returns the set of tags that apply to ALL the given hosts; :rel => :or returns the set of tags that apply to ANY of the given hosts. Default: :rel => :or.

  • :include_skip? - flag indicating whether to include hosts that have the SKIP tag set. Default: false i.e. omit hosts tagged with SKIP.



36
37
38
39
# File 'lib/hosttag.rb', line 36

def hosttag_lookup_hosts(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  return lookup_keys(args, options.merge({ :type => :host }))
end

#hosttag_lookup_tags(*args) ⇒ Object

Lookup the given tag(s), returning an array of hosts to which they apply. If multiple tags are given, by default the list of hosts is those to which ALL of the tags apply i.e. results are ANDed or intersected. To change this, pass :rel => :or in the options hash. The final argument may be an options hash, which accepts the following keys:

  • :rel - either :and or :or, specifying the relationship to use when interpreting the set of tags. :rel => :and returns the set of hosts to which ALL the given tags apply; :rel => :or returns the set of hosts to which ANY of the tags apply. Default: :rel => :and.

  • :include_skip? - flag indicating whether to include hosts that have the SKIP tag set. Default: false i.e. omit hosts tagged with SKIP.



18
19
20
21
# File 'lib/hosttag.rb', line 18

def hosttag_lookup_tags(*args)
  options = args.last.is_a?(Hash) ? args.pop : {}
  return lookup_keys(args, options.merge({ :type => :tag }))
end

#hosttag_truncate(options) ⇒ Object

Delete all hosts and tags in the hosttag datastore. This is the nuclear option, used in hosttag_import_from_directory if :delete => true. Interactively confirms unless the :autoconfirm option is set. The final argument may be an options hash, which accepts the following keys:

  • :autoconfirm - if true, truncate without asking for any confirmation



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/hosttag.rb', line 227

def hosttag_truncate(options)
  if not options[:autoconfirm]
    print "Do you really want to delete EVERYTHING from your datastore? [yN] "
    $stdout.flush
    confirm = $stdin.gets.chomp
    return unless confirm =~ %r{^y}i
  end

  r = hosttag_server(options)
  r.keys(r.get_key("*")).each { |k| r.del(k) }
end