hosttag 0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/ChangeLog +44 -0
- data/EXCLUDE +3 -0
- data/LICENCE +674 -0
- data/README +92 -0
- data/Rakefile +30 -0
- data/TODO +4 -0
- data/bin/hosttag +168 -0
- data/bin/ht +168 -0
- data/bin/htdel +164 -0
- data/bin/htdump +70 -0
- data/bin/htexport +99 -0
- data/bin/htimport +81 -0
- data/bin/htremap +93 -0
- data/bin/htset +164 -0
- data/etc/Makefile +16 -0
- data/etc/README +10 -0
- data/hosttag.gemspec +23 -0
- data/hosttag.spec +179 -0
- data/lib/hosttag.rb +402 -0
- data/lib/hosttag/server.rb +35 -0
- data/test/data_hosttag/a/centos +0 -0
- data/test/data_hosttag/a/centos5 +0 -0
- data/test/data_hosttag/a/centos5-x86_64 +0 -0
- data/test/data_hosttag/a/public +0 -0
- data/test/data_hosttag/g/SKIP +0 -0
- data/test/data_hosttag/g/centos +0 -0
- data/test/data_hosttag/g/centos4 +0 -0
- data/test/data_hosttag/g/centos4-i386 +0 -0
- data/test/data_hosttag/h/SKIP +0 -0
- data/test/data_hosttag/h/centos +0 -0
- data/test/data_hosttag/h/centos4 +0 -0
- data/test/data_hosttag/h/centos4-x86_64 +0 -0
- data/test/data_hosttag/h/public +0 -0
- data/test/data_hosttag/m/centos +0 -0
- data/test/data_hosttag/m/centos4 +0 -0
- data/test/data_hosttag/m/centos4-x86_64 +0 -0
- data/test/data_hosttag/m/public +0 -0
- data/test/data_hosttag/m/vps +0 -0
- data/test/data_hosttag/n/centos +0 -0
- data/test/data_hosttag/n/centos5 +0 -0
- data/test/data_hosttag/n/centos5-i386 +0 -0
- data/test/data_hosttag/n/laptop +0 -0
- data/test/test_hosttag_bin.rb +70 -0
- data/test/test_hosttag_lib.rb +119 -0
- data/test/test_htset_bin.rb +174 -0
- data/test/test_htset_lib.rb +183 -0
- data/test/ts_all.rb +4 -0
- metadata +132 -0
data/README
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
Introduction
|
2
|
+
------------
|
3
|
+
|
4
|
+
Hosttag is a client for tagging hostnames into groups or classes, storing
|
5
|
+
the mappings in a redis datastore. It requires the 'redis' rubygem.
|
6
|
+
|
7
|
+
|
8
|
+
Hosttag assumes your redis server is found on a server called 'hosttag' on
|
9
|
+
the default redis port (6379). You can either setup 'hosttag' as a hostname
|
10
|
+
alias in a local hosts file, or specify an alternative host to use in a
|
11
|
+
HOSTTAG_SERVER environment variable, or explicitly via a -s/--server
|
12
|
+
<hostname> argument. Similarly, an alternate port can be specified using a
|
13
|
+
HOSTTAG_PORT environment variable, or a -p/--port <port> command line
|
14
|
+
argument.
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
Basic Usage
|
19
|
+
-----------
|
20
|
+
|
21
|
+
# Set some tags (-T says assume anything you don't know is a tag)
|
22
|
+
$ htset -T zeus dell centos centos5 server
|
23
|
+
$ htset -T minerva dell centos centos6 laptop
|
24
|
+
$ htset -T hermes hp ubuntu lucid desktop
|
25
|
+
$ htset -T athena hp ubuntu maverick server
|
26
|
+
|
27
|
+
|
28
|
+
# Queries
|
29
|
+
$ ht dell
|
30
|
+
minerva zeus
|
31
|
+
|
32
|
+
$ ht ubuntu
|
33
|
+
hermes athena
|
34
|
+
|
35
|
+
$ ht -o hp dell # -o/--or ORs arguments
|
36
|
+
hermes minerva zeus
|
37
|
+
|
38
|
+
$ ht -a ubuntu server # -a/--and ANDs arguments
|
39
|
+
athena
|
40
|
+
|
41
|
+
$ ht -A # all hosts
|
42
|
+
athena hermes minerva zeus
|
43
|
+
|
44
|
+
$ ht -T # all tags
|
45
|
+
centos centos5 centos6 dell desktop hp laptop lucid maverick server
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
Security
|
51
|
+
--------
|
52
|
+
|
53
|
+
A warning on security: basically, there isn't any. Anyone who can get write
|
54
|
+
to your redis server can write, rewrite, and delete all your hosttags. So
|
55
|
+
you should only use hosttag on a trusted network.
|
56
|
+
|
57
|
+
Redis supports a password-ed mode, and it might make sense to use that via
|
58
|
+
a config file or something, so you could at least restrict usage via file
|
59
|
+
permissions (although traffic would still be in the clear, I believe). For
|
60
|
+
now, though, there's no security.
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
Installation
|
65
|
+
------------
|
66
|
+
|
67
|
+
To install from source, unpack the tarball and run 'rake install' (requires
|
68
|
+
rubygems and rake). You can set GEM_HOME to an alternate install directory
|
69
|
+
(e.g. ~/.gem) if you want to install somewhere other than your gem default.
|
70
|
+
|
71
|
+
RPMs are available for CentOS/RHEL {5,6} from the Open Fusion repository:
|
72
|
+
|
73
|
+
http://www.openfusion.net/linux/openfusion_rpm_repository
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
Author
|
79
|
+
------
|
80
|
+
|
81
|
+
Gavin Carr <gavin@openfusion.com.au>, http://www.openfusion.net/
|
82
|
+
|
83
|
+
Copyright 2010-2011 Gavin Carr.
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
Licence
|
88
|
+
-------
|
89
|
+
|
90
|
+
This software is free software, licensed under the GNU Public License v3
|
91
|
+
or any later version.
|
92
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
desc 'Run tests'
|
3
|
+
task :test do
|
4
|
+
system 'ruby -I lib -I test test/ts_all.rb'
|
5
|
+
end
|
6
|
+
|
7
|
+
gemspec = eval(File.read(Dir["*.gemspec"].first))
|
8
|
+
|
9
|
+
desc "Validate the gemspec"
|
10
|
+
task :validate do
|
11
|
+
gemspec.validate
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Build gem locally"
|
15
|
+
task :build => :validate do
|
16
|
+
system "gem build #{gemspec.name}.gemspec"
|
17
|
+
FileUtils.mkdir_p "pkg"
|
18
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", "pkg"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Install gem locally"
|
22
|
+
task :install => :build do
|
23
|
+
system "gem install pkg/#{gemspec.name}-#{gemspec.version}"
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Clean automatically generated files"
|
27
|
+
task :clean do
|
28
|
+
FileUtils.rm_rf "pkg"
|
29
|
+
end
|
30
|
+
|
data/TODO
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
- ht -l -A doesn't work as expected
|
2
|
+
- ht -l -T doesn't work as expected
|
3
|
+
- it would be nice to able to do 'ht -x usd001 na', 'ht -X ignore_file na', 'ht -x usd001 -A'
|
4
|
+
- it would also be nice to able to do 'ht -n dell -n supermicro -A', 'ht -n dell server', 'ht -N not_file -A', 'ht -N not_file server'
|
data/bin/hosttag
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# hosttag query client
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ht <tag>
|
7
|
+
#
|
8
|
+
# ht [-a] <tag1> <tag2> Show hosts with tag1 AND tag2 (intersection, default)
|
9
|
+
# ht -o <tag1> <tag2> Show hosts with tag1 OR tag2 (union)
|
10
|
+
#
|
11
|
+
# ht -A Show all hosts
|
12
|
+
#
|
13
|
+
# ht -t <host> Show tags on 'host'
|
14
|
+
# ht -t [-o] <host1> <host2> Show tags on 'host' OR 'host2' (union, default)
|
15
|
+
# ht -t -a <host1> <host2> Show tags on 'host' AND 'host2' (intersection)
|
16
|
+
#
|
17
|
+
# ht -T Show all tags
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'optparse'
|
21
|
+
|
22
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
23
|
+
require 'hosttag'
|
24
|
+
include Hosttag
|
25
|
+
|
26
|
+
# -------------------------------------------------------------------------
|
27
|
+
# Subroutines
|
28
|
+
|
29
|
+
def die(error)
|
30
|
+
warn error
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def usage(opts, msg = nil)
|
35
|
+
if msg
|
36
|
+
warn msg
|
37
|
+
$stderr.puts
|
38
|
+
end
|
39
|
+
warn opts
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
def render(results, options, arg = nil)
|
44
|
+
return unless results and results.length > 0
|
45
|
+
# Report
|
46
|
+
if options[:list]
|
47
|
+
puts "#{arg}: #{results.sort.join(' ')}"
|
48
|
+
else
|
49
|
+
puts results.sort.join(options[:join])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# -------------------------------------------------------------------------
|
54
|
+
# Main
|
55
|
+
|
56
|
+
options = { :join => ' ' }
|
57
|
+
|
58
|
+
opts = OptionParser.new
|
59
|
+
opts.banner = "Usage: hosttag [options] <tag> [<tag2>...]"
|
60
|
+
opts.on('-?', '-h', '--help') do
|
61
|
+
puts opts
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
opts.on('-a', '--and', 'Report hosts with ALL the given tags (AND result sets)') do
|
65
|
+
options[:rel] = :and
|
66
|
+
end
|
67
|
+
opts.on('-o', '--or', 'Report hosts with ANY of the given tags (OR result sets)') do
|
68
|
+
options[:rel] = :or
|
69
|
+
end
|
70
|
+
opts.on('-l', '--list', "List hosts/tags separately for each tag/host") do
|
71
|
+
options[:list] = 1
|
72
|
+
end
|
73
|
+
opts.on('-t', '--tag', '--tags', "Tag mode: report tags for the given hosts") do
|
74
|
+
options[:type] = :host
|
75
|
+
end
|
76
|
+
opts.on('-A', '--all', 'Report all hosts') do
|
77
|
+
if not options[:all]
|
78
|
+
options[:all] = :hosts
|
79
|
+
elsif options[:all] == :hosts
|
80
|
+
options[:include_skip?] ||= true
|
81
|
+
else
|
82
|
+
usage(opts, "Error: can't specify both -A/--all-hosts and -T/--all-tags")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
opts.on('-T', '--all-tags', 'Report all tags') do
|
86
|
+
if not options[:all]
|
87
|
+
options[:all] = :tags
|
88
|
+
elsif options[:all] == :tags
|
89
|
+
options[:include_skip?] ||= true
|
90
|
+
else
|
91
|
+
usage(opts, "Error: can't specify both -A/--all-hosts and -T/--all-tags")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
opts.on('-1', 'List one item per line') do
|
95
|
+
options[:one] = 1
|
96
|
+
options[:join] = "\n"
|
97
|
+
end
|
98
|
+
opts.on('-d', '--delimiter=STR', String, 'Delimiter for reporting (default " ")') do |val|
|
99
|
+
options[:join] = val
|
100
|
+
end
|
101
|
+
opts.on('-s=ARG', '--server=ARG', String, 'Server hostname to connect to. Default: hosttag') do |val|
|
102
|
+
options[:server] = val
|
103
|
+
end
|
104
|
+
opts.on('-p=ARG', '--port=ARG', Integer, 'Server port to connect to') do |val|
|
105
|
+
options[:port] = val
|
106
|
+
end
|
107
|
+
opts.on('--ns=STR', '--namespace=STR', String, 'Namespace into which we load hosttag data. Default: hosttag') do |val|
|
108
|
+
options[:namespace] = val
|
109
|
+
end
|
110
|
+
opts.on('-v', '--verbose', 'Verbose output') do
|
111
|
+
options[:verbose] = true
|
112
|
+
end
|
113
|
+
|
114
|
+
# Parse options
|
115
|
+
begin
|
116
|
+
args = opts.parse(ARGV)
|
117
|
+
rescue => e
|
118
|
+
usage(opts, "Error: " << e)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Sanity checks
|
122
|
+
if args.length == 0 and not options[:all]
|
123
|
+
usage(opts)
|
124
|
+
end
|
125
|
+
if args.length > 0 and options[:all]
|
126
|
+
if options[:all] == :hosts
|
127
|
+
error = "Error: can't specify hosts with -A/--all-hosts"
|
128
|
+
else
|
129
|
+
error = "Error: can't specify tags with -T/--all-tags"
|
130
|
+
end
|
131
|
+
usage(opts, error)
|
132
|
+
end
|
133
|
+
if options[:list] and options[:one]
|
134
|
+
usage(opts, "Warning: -L/--list and -1 are incompatible")
|
135
|
+
end
|
136
|
+
|
137
|
+
# All request
|
138
|
+
if options[:all]
|
139
|
+
if options[:all] == :hosts
|
140
|
+
all = hosttag_all_hosts(options)
|
141
|
+
else
|
142
|
+
all = hosttag_all_tags(options)
|
143
|
+
end
|
144
|
+
if options[:list]
|
145
|
+
args = all
|
146
|
+
else
|
147
|
+
render all, options
|
148
|
+
exit
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Standard request
|
153
|
+
# In list mode, process all args separately; otherwise, do them all at once
|
154
|
+
if options[:list]
|
155
|
+
arg_list = args
|
156
|
+
else
|
157
|
+
arg_list = [ args ]
|
158
|
+
end
|
159
|
+
|
160
|
+
arg_list.each do |a|
|
161
|
+
begin
|
162
|
+
results = hosttag_lookup(a, options)
|
163
|
+
render results, options, a
|
164
|
+
rescue => e
|
165
|
+
warn e
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
data/bin/ht
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# hosttag query client
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ht <tag>
|
7
|
+
#
|
8
|
+
# ht [-a] <tag1> <tag2> Show hosts with tag1 AND tag2 (intersection, default)
|
9
|
+
# ht -o <tag1> <tag2> Show hosts with tag1 OR tag2 (union)
|
10
|
+
#
|
11
|
+
# ht -A Show all hosts
|
12
|
+
#
|
13
|
+
# ht -t <host> Show tags on 'host'
|
14
|
+
# ht -t [-o] <host1> <host2> Show tags on 'host' OR 'host2' (union, default)
|
15
|
+
# ht -t -a <host1> <host2> Show tags on 'host' AND 'host2' (intersection)
|
16
|
+
#
|
17
|
+
# ht -T Show all tags
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'optparse'
|
21
|
+
|
22
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
23
|
+
require 'hosttag'
|
24
|
+
include Hosttag
|
25
|
+
|
26
|
+
# -------------------------------------------------------------------------
|
27
|
+
# Subroutines
|
28
|
+
|
29
|
+
def die(error)
|
30
|
+
warn error
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def usage(opts, msg = nil)
|
35
|
+
if msg
|
36
|
+
warn msg
|
37
|
+
$stderr.puts
|
38
|
+
end
|
39
|
+
warn opts
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
def render(results, options, arg = nil)
|
44
|
+
return unless results and results.length > 0
|
45
|
+
# Report
|
46
|
+
if options[:list]
|
47
|
+
puts "#{arg}: #{results.sort.join(' ')}"
|
48
|
+
else
|
49
|
+
puts results.sort.join(options[:join])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# -------------------------------------------------------------------------
|
54
|
+
# Main
|
55
|
+
|
56
|
+
options = { :join => ' ' }
|
57
|
+
|
58
|
+
opts = OptionParser.new
|
59
|
+
opts.banner = "Usage: hosttag [options] <tag> [<tag2>...]"
|
60
|
+
opts.on('-?', '-h', '--help') do
|
61
|
+
puts opts
|
62
|
+
exit
|
63
|
+
end
|
64
|
+
opts.on('-a', '--and', 'Report hosts with ALL the given tags (AND result sets)') do
|
65
|
+
options[:rel] = :and
|
66
|
+
end
|
67
|
+
opts.on('-o', '--or', 'Report hosts with ANY of the given tags (OR result sets)') do
|
68
|
+
options[:rel] = :or
|
69
|
+
end
|
70
|
+
opts.on('-l', '--list', "List hosts/tags separately for each tag/host") do
|
71
|
+
options[:list] = 1
|
72
|
+
end
|
73
|
+
opts.on('-t', '--tag', '--tags', "Tag mode: report tags for the given hosts") do
|
74
|
+
options[:type] = :host
|
75
|
+
end
|
76
|
+
opts.on('-A', '--all', 'Report all hosts') do
|
77
|
+
if not options[:all]
|
78
|
+
options[:all] = :hosts
|
79
|
+
elsif options[:all] == :hosts
|
80
|
+
options[:include_skip?] ||= true
|
81
|
+
else
|
82
|
+
usage(opts, "Error: can't specify both -A/--all-hosts and -T/--all-tags")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
opts.on('-T', '--all-tags', 'Report all tags') do
|
86
|
+
if not options[:all]
|
87
|
+
options[:all] = :tags
|
88
|
+
elsif options[:all] == :tags
|
89
|
+
options[:include_skip?] ||= true
|
90
|
+
else
|
91
|
+
usage(opts, "Error: can't specify both -A/--all-hosts and -T/--all-tags")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
opts.on('-1', 'List one item per line') do
|
95
|
+
options[:one] = 1
|
96
|
+
options[:join] = "\n"
|
97
|
+
end
|
98
|
+
opts.on('-d', '--delimiter=STR', String, 'Delimiter for reporting (default " ")') do |val|
|
99
|
+
options[:join] = val
|
100
|
+
end
|
101
|
+
opts.on('-s=ARG', '--server=ARG', String, 'Server hostname to connect to. Default: hosttag') do |val|
|
102
|
+
options[:server] = val
|
103
|
+
end
|
104
|
+
opts.on('-p=ARG', '--port=ARG', Integer, 'Server port to connect to') do |val|
|
105
|
+
options[:port] = val
|
106
|
+
end
|
107
|
+
opts.on('--ns=STR', '--namespace=STR', String, 'Namespace into which we load hosttag data. Default: hosttag') do |val|
|
108
|
+
options[:namespace] = val
|
109
|
+
end
|
110
|
+
opts.on('-v', '--verbose', 'Verbose output') do
|
111
|
+
options[:verbose] = true
|
112
|
+
end
|
113
|
+
|
114
|
+
# Parse options
|
115
|
+
begin
|
116
|
+
args = opts.parse(ARGV)
|
117
|
+
rescue => e
|
118
|
+
usage(opts, "Error: " << e)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Sanity checks
|
122
|
+
if args.length == 0 and not options[:all]
|
123
|
+
usage(opts)
|
124
|
+
end
|
125
|
+
if args.length > 0 and options[:all]
|
126
|
+
if options[:all] == :hosts
|
127
|
+
error = "Error: can't specify hosts with -A/--all-hosts"
|
128
|
+
else
|
129
|
+
error = "Error: can't specify tags with -T/--all-tags"
|
130
|
+
end
|
131
|
+
usage(opts, error)
|
132
|
+
end
|
133
|
+
if options[:list] and options[:one]
|
134
|
+
usage(opts, "Warning: -L/--list and -1 are incompatible")
|
135
|
+
end
|
136
|
+
|
137
|
+
# All request
|
138
|
+
if options[:all]
|
139
|
+
if options[:all] == :hosts
|
140
|
+
all = hosttag_all_hosts(options)
|
141
|
+
else
|
142
|
+
all = hosttag_all_tags(options)
|
143
|
+
end
|
144
|
+
if options[:list]
|
145
|
+
args = all
|
146
|
+
else
|
147
|
+
render all, options
|
148
|
+
exit
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Standard request
|
153
|
+
# In list mode, process all args separately; otherwise, do them all at once
|
154
|
+
if options[:list]
|
155
|
+
arg_list = args
|
156
|
+
else
|
157
|
+
arg_list = [ args ]
|
158
|
+
end
|
159
|
+
|
160
|
+
arg_list.each do |a|
|
161
|
+
begin
|
162
|
+
results = hosttag_lookup(a, options)
|
163
|
+
render results, options, a
|
164
|
+
rescue => e
|
165
|
+
warn e
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|