iqeo-hostspec 0.1.0.pre1 → 0.1.0.pre2
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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/README.md +3 -7
- data/bin/hostspec +1 -126
- data/lib/iqeo/hostspec/hostspec.rb +179 -0
- data/lib/iqeo/hostspec/runner.rb +131 -0
- data/lib/iqeo/hostspec/version.rb +7 -0
- data/lib/iqeo/hostspec.rb +3 -178
- data/spec/iqeo/hostspec_spec.rb +47 -46
- data/spec/{hostspec_runner_spec.rb → iqeo/runner_spec.rb} +3 -2
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b3e2f1c15026a95d95666c9eaee3de460ccf92d
|
4
|
+
data.tar.gz: 263459817766d46b9b32d4bfee19f546134817b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0be30e4d2da2a60e74561d4a224e8cf85c4f3d5477ede05859aa6a62f58983122d907489920071acd200bd19dfd53bef9ecd3594b576354ac102b61b70436404
|
7
|
+
data.tar.gz: a07aabde5df2381c9d2f4b379a16e8c758c422c773e16d665b3d2af1ff84ef2b85876cd0c54fb55a72d2e87512782d5212847735fd216f4b2bac1c64acc66904
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Iqeo::Hostspec
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/iqeo-hostspec)
|
4
|
+
[](https://travis-ci.org/iqeo/iqeo-hostspec)
|
5
|
+
|
3
6
|
TODO: Write a gem description
|
4
7
|
|
5
8
|
## Installation
|
@@ -20,10 +23,3 @@ Or install it yourself as:
|
|
20
23
|
|
21
24
|
TODO: Write usage instructions here
|
22
25
|
|
23
|
-
## Contributing
|
24
|
-
|
25
|
-
1. Fork it
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create new Pull Request
|
data/bin/hostspec
CHANGED
@@ -2,130 +2,5 @@
|
|
2
2
|
|
3
3
|
require 'iqeo/hostspec'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
def self.run args, out: $stdout, err: $stderr
|
8
|
-
|
9
|
-
if args.empty?
|
10
|
-
err.puts "Error: No specs given"
|
11
|
-
return 1
|
12
|
-
end
|
13
|
-
|
14
|
-
if args.include?('-h') || args.include?('--help')
|
15
|
-
print_help err
|
16
|
-
return 0
|
17
|
-
end
|
18
|
-
|
19
|
-
if args.include?('-v') || args.include?('--version')
|
20
|
-
print_version err
|
21
|
-
return 0
|
22
|
-
end
|
23
|
-
|
24
|
-
cmd_sw_index = args.index('-c') || args.index('--cmd')
|
25
|
-
if cmd_sw_index
|
26
|
-
specs = args.take(cmd_sw_index) # specs are before switch
|
27
|
-
cmd = args.drop(cmd_sw_index+1).join ' ' # command components are after switch, join into string to pass to subshell
|
28
|
-
if cmd.empty?
|
29
|
-
err.puts "Error: No command given"
|
30
|
-
return 2
|
31
|
-
end
|
32
|
-
else
|
33
|
-
specs = args
|
34
|
-
cmd = nil
|
35
|
-
end
|
36
|
-
|
37
|
-
results = []
|
38
|
-
specs.each do |spec|
|
39
|
-
spec = spec.strip
|
40
|
-
begin
|
41
|
-
host_spec = Iqeo::Hostspec.new spec
|
42
|
-
rescue Exception => e
|
43
|
-
err.puts "Error: #{e.message}"
|
44
|
-
return 3
|
45
|
-
end
|
46
|
-
if cmd.nil?
|
47
|
-
host_spec.each { |address| out.puts address }
|
48
|
-
else
|
49
|
-
env = {
|
50
|
-
'HOSTSPEC_MASK' => host_spec.mask,
|
51
|
-
'HOSTSPEC_MASKLEN' => host_spec.mask_length.to_s,
|
52
|
-
'HOSTSPEC_COUNT' => host_spec.size.to_s
|
53
|
-
}
|
54
|
-
host_spec.each_with_index do |address,index|
|
55
|
-
env['HOSTSPEC_IP'] = address
|
56
|
-
env['HOSTSPEC_INDEX'] = (index+1).to_s
|
57
|
-
# this uses posix 'sh', to use bash user's cmd should be 'bash -c "echo \$HOSTSPEC_IP"' and deal with the weird nested quoting
|
58
|
-
results << system( env, cmd )
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
( results.empty? || results.all? ) ? 0 : 4
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.print_help io
|
66
|
-
io.puts "Usage: hostspec [ options ] specs... [ [ -c / --cmd ] command ]"
|
67
|
-
io.puts
|
68
|
-
io.puts "Prints all IP addresses for IP host specifications (see Specs:)."
|
69
|
-
io.puts "If specified, a command is executed for each IP address, with related values in environment variables (see Command:)."
|
70
|
-
io.puts
|
71
|
-
io.puts "Specs:"
|
72
|
-
io.puts " Nmap-style IP host specifications, multiple specs separated by spaces."
|
73
|
-
io.puts " Single host : x.x.x.x or hostname"
|
74
|
-
io.puts " Multiple hosts:"
|
75
|
-
io.puts " - by mask length : x.x.x.x/m or hostname/m"
|
76
|
-
io.puts " - by octet values : x.x.x.a,b,c"
|
77
|
-
io.puts " - by octet ranges : x.x.x.d-e"
|
78
|
-
io.puts " Octet values and ranges may be combined or applied to any/multiple octets."
|
79
|
-
io.puts " Examples:"
|
80
|
-
io.puts " hostname : localhost => 127.0.0.1"
|
81
|
-
io.puts " hostname w/mask : localhost/24 => 127.0.0.0 127.0.0.1 ... 127.0.0.254 127.0.0.255"
|
82
|
-
io.puts " address : 1.1.1.1 => 1.1.1.1"
|
83
|
-
io.puts " address w/mask : 2.2.2.1/24 => 2.2.2.0 2.2.2.1 ... 2.2.2.254 2.2.2.255"
|
84
|
-
io.puts " address w/values : 3.3.3.10,20,30 => 3.3.3.10 3.3.3.20 3.3.3.30"
|
85
|
-
io.puts " address w/ranges : 4.4.4.40-50 => 4.4.4.40 4.4.4.41 ... 4.4.4.49 4.4.4.50"
|
86
|
-
io.puts " address w/combo : 5.5.5.2,4-6,8 => 5.5.5.2 5.5.5.4 5.5.5.5 5.5.5.6 5.5.5.8"
|
87
|
-
io.puts " complex : 6.1-2,3.4-5.6 => 6.1.4.6 6.1.5.6 6.2.4.6 6.2.5.6 6.3.4.6 6.3.5.6"
|
88
|
-
io.puts
|
89
|
-
io.puts "Command:"
|
90
|
-
io.puts " A command to execute for each IP address may be specified following the command switch ( -c / --cmd )."
|
91
|
-
io.puts " The command is executed in a separate shell for each IP address."
|
92
|
-
io.puts " Environment variables are provided with values for each IP address command execution."
|
93
|
-
io.puts " Quote these variables in the command to prevent substitution by the current shell."
|
94
|
-
io.puts " $HOSTSPEC_IP : IP address"
|
95
|
-
io.puts " $HOSTSPEC_MASK : Mask (255.255.255.255 if a mask length was not specified)"
|
96
|
-
io.puts " $HOSTSPEC_MASKLEN : Mask length (32 if a mask length was not specified)"
|
97
|
-
io.puts " $HOSTSPEC_COUNT : Count of IP addresses"
|
98
|
-
io.puts " $HOSTSPEC_INDEX : Index of IP address (from 1 to Count)"
|
99
|
-
io.puts " Examples:"
|
100
|
-
io.puts " Print IP addresses and mask length with index and count:"
|
101
|
-
io.puts " hostspec 1.1.1.1-3 --cmd echo '$HOSTSPEC_INDEX' of '$HOSTSPECT_COUNT' : '$HOSTSPEC_IP/$HOSTSPEC_MASKLEN'"
|
102
|
-
io.puts " ..."
|
103
|
-
io.puts " 1 of 3 : 1.1.1.1/24"
|
104
|
-
io.puts " 2 of 3 : 1.1.1.2/24"
|
105
|
-
io.puts " 3 of 3 : 1.1.1.3/24"
|
106
|
-
io.puts " Collect routing tables of all hosts on a network via ssh:"
|
107
|
-
io.puts " hostspec 1.1.1.1-254 --cmd 'ssh me@$HOSTSPEC_IP route -n'"
|
108
|
-
io.puts " Collect default web pages from all servers on a network via curl:"
|
109
|
-
io.puts " hostspec 1.1.1.1-254 --cmd curl -o '$HOSTSPEC_IP.html' 'http://$HOSTSPEC_IP'"
|
110
|
-
io.puts " Collect IP configuration info from multiple windows systems (run from a windows system):"
|
111
|
-
io.puts " hostspec 1.1.1.1-254 --cmd psexec '\\%HOSTSPEC_IP%' ipconfig /all"
|
112
|
-
io.puts " Collect IP configuration info from multiple windows systems (run from a linux system with kerberos):"
|
113
|
-
io.puts " hostspec 1.1.1.1-254 --cmd winexe --kerberos yes //$(dig -x '$HOSTSPEC_IP' +short) ipconfig /all"
|
114
|
-
io.puts " ...or any task that you would have to execute individually on multiple systems."
|
115
|
-
io.puts
|
116
|
-
io.puts "Options:"
|
117
|
-
io.puts " -h / --help : Display this helpful information"
|
118
|
-
io.puts " -v / --version : Display program version"
|
119
|
-
io.puts
|
120
|
-
end
|
121
|
-
|
122
|
-
def self.print_version io
|
123
|
-
io.puts "hostspec version #{Iqeo::Hostspec::VERSION}"
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
if __FILE__ == $0
|
129
|
-
exit Runner.run ARGV
|
130
|
-
end
|
5
|
+
exit Iqeo::Hostspec::Runner.run ARGV
|
131
6
|
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
module Iqeo
|
4
|
+
module Hostspec
|
5
|
+
|
6
|
+
class HostspecException < Exception ; end
|
7
|
+
|
8
|
+
class Hostspec
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :string, :mask, :mask_length, :address_spec, :hostname
|
13
|
+
|
14
|
+
def initialize spec_str
|
15
|
+
@string = spec_str
|
16
|
+
raise HostspecException, 'spec cannot be empty' if spec_str.empty?
|
17
|
+
host_str, mask_str = split_on_slash spec_str
|
18
|
+
raise HostspecException, 'host cannot be empty' if host_str.empty?
|
19
|
+
parse_mask mask_str
|
20
|
+
begin
|
21
|
+
parse_address_spec host_str
|
22
|
+
rescue HostspecException
|
23
|
+
parse_hostname host_str
|
24
|
+
end
|
25
|
+
raise HostspecException, 'complex spec cannot have mask length' if @mask_specified && @address_spec.any? { |octet| octet.size > 1 }
|
26
|
+
mask_address_spec
|
27
|
+
end
|
28
|
+
|
29
|
+
def split_on_slash str
|
30
|
+
case str.count '/'
|
31
|
+
when 0 then [ str.strip, '' ]
|
32
|
+
when 1 then str.strip.split '/'
|
33
|
+
else raise 'bad format, expected 0 or 1 "/"'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_mask str
|
38
|
+
if str.empty?
|
39
|
+
@mask = '255.255.255.255'
|
40
|
+
@mask_length = 32
|
41
|
+
@mask_specified = false
|
42
|
+
return
|
43
|
+
end
|
44
|
+
if match = str.match( /^\d+$/ )
|
45
|
+
@mask_length = match[0].to_i
|
46
|
+
raise "bad mask length (#{@mask_length}), expected between 0 ad 32" unless @mask_length.between? 0,32
|
47
|
+
mask_int = ((2**@mask_length)-1) << (32-@mask_length)
|
48
|
+
@mask = [24,16,8,0].collect { |n| ( mask_int & ( 255 << n ) ) >> n }.join '.'
|
49
|
+
@mask_specified = true
|
50
|
+
else
|
51
|
+
raise "bad format, expected mask length after '/'"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def mask_address_spec
|
56
|
+
@address_spec.each_with_index do |octet,index|
|
57
|
+
high_bit_position = ( index * 8 ) + 1
|
58
|
+
low_bit_position = ( index + 1 ) * 8
|
59
|
+
@address_spec[index] = case
|
60
|
+
when @mask_length >= low_bit_position then octet
|
61
|
+
when @mask_length < high_bit_position then [0..255]
|
62
|
+
else
|
63
|
+
octet_mask_length = @mask_length % 8
|
64
|
+
octet_mask = ( ( 2 ** octet_mask_length ) - 1 ) << ( 8 - octet_mask_length )
|
65
|
+
octet_mask_inverted = octet_mask ^ 255
|
66
|
+
octet_min = octet_mask & octet[0]
|
67
|
+
octet_max = octet_min | octet_mask_inverted
|
68
|
+
[octet_min..octet_max]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_address_spec str
|
74
|
+
octet_strs = str.split '.'
|
75
|
+
raise HostspecException, 'bad ip, expected 4 octets' unless octet_strs.size == 4
|
76
|
+
octets = octet_strs.collect { |octet_str| parse_octet octet_str }
|
77
|
+
@address_spec = octets
|
78
|
+
end
|
79
|
+
|
80
|
+
def parse_octet str
|
81
|
+
values = str.split ','
|
82
|
+
values.collect { |value_str| parse_octet_value value_str }
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_octet_value str
|
86
|
+
# values may be dash denoted ranges, possibilities...
|
87
|
+
# n : just a number : 'n'.split '-' == ['n'] <= same = problem! 'n'.split '-', -1 == [ "n" ]
|
88
|
+
# n-m : range from n to m : 'n-m'.split '-' == ['n','m'] 'n-m'.split '-', -1 == [ "n" , "m" ]
|
89
|
+
# n- : range from n to 255 : 'n-'.split '-' == ['n'] <= same = problem! 'n-'.split '-', -1 == [ "n" , "" ]
|
90
|
+
# -m : range from 0 to m : '-m'.split '-' == ['','m'] '-m'.split '-', -1 == [ "" , "m" ]
|
91
|
+
# - : range from 0 to 255 : '-'.split '-' == [] '-'.split '-', -1 == [ "" , "" ]
|
92
|
+
numbers = str.split '-', -1 # maximize return fields to distinguish 'n' from '-m'
|
93
|
+
case numbers.size
|
94
|
+
when 1 then
|
95
|
+
check_octet_value numbers[0]
|
96
|
+
numbers[0].to_i
|
97
|
+
when 2 then
|
98
|
+
numbers[0] = '0' if numbers[0].empty?
|
99
|
+
numbers[1] = '255' if numbers[1].empty?
|
100
|
+
check_octet_value numbers[0]
|
101
|
+
check_octet_value numbers[1]
|
102
|
+
range_start = numbers[0].to_i
|
103
|
+
range_finish = numbers[1].to_i
|
104
|
+
raise HostspecException, "bad ip, reversed range in octet value: #{str}" if range_start > range_finish
|
105
|
+
range_start..range_finish
|
106
|
+
else
|
107
|
+
raise HostspecException, "bad ip, invalid octet value: #{str}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_octet_value str
|
112
|
+
match = str.match /^(25[0-5]|2[0-4]\d|[0-1]\d\d|\d\d|\d)$/
|
113
|
+
raise HostspecException, "bad ip, octet value is not a number in 0-255: #{str}" unless match
|
114
|
+
end
|
115
|
+
|
116
|
+
def parse_hostname str
|
117
|
+
@hostname = str
|
118
|
+
parse_address_spec Resolv.getaddress(str)
|
119
|
+
end
|
120
|
+
|
121
|
+
def recursively_iterate_octets octet_index = 0, address = [], &block
|
122
|
+
@address_spec[octet_index].each do |item|
|
123
|
+
if item.respond_to? :each
|
124
|
+
item.each do |value|
|
125
|
+
address.push value
|
126
|
+
octet_index == 3 ? yield( address.join '.' ) : recursively_iterate_octets( octet_index + 1, address, &block )
|
127
|
+
address.pop
|
128
|
+
end
|
129
|
+
else
|
130
|
+
address.push item
|
131
|
+
octet_index == 3 ? yield( address.join '.' ) : recursively_iterate_octets( octet_index + 1, address, &block )
|
132
|
+
address.pop
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def each_address
|
138
|
+
if block_given?
|
139
|
+
recursively_iterate_octets do |address_str|
|
140
|
+
yield address_str
|
141
|
+
end
|
142
|
+
else
|
143
|
+
return to_enum( :each_address ) { size }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
alias_method :each, :each_address
|
148
|
+
|
149
|
+
def size
|
150
|
+
if @mask_length == 32
|
151
|
+
@address_spec.inject(1) { |oc,o| oc * o.inject(0) { |vc,v| vc + ( v.respond_to?(:each) ? v.size : 1 ) } }
|
152
|
+
else
|
153
|
+
2**(32-@mask_length)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def min
|
158
|
+
first
|
159
|
+
end
|
160
|
+
|
161
|
+
def last
|
162
|
+
address = nil
|
163
|
+
each { |addr| address = addr }
|
164
|
+
address
|
165
|
+
end
|
166
|
+
|
167
|
+
def max
|
168
|
+
last
|
169
|
+
end
|
170
|
+
|
171
|
+
def minmax
|
172
|
+
[first,last]
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
module Iqeo
|
3
|
+
module Hostspec
|
4
|
+
|
5
|
+
class Runner
|
6
|
+
|
7
|
+
def self.run args, out: $stdout, err: $stderr
|
8
|
+
|
9
|
+
if args.empty?
|
10
|
+
err.puts "Error: No specs given"
|
11
|
+
return 1
|
12
|
+
end
|
13
|
+
|
14
|
+
if args.include?('-h') || args.include?('--help')
|
15
|
+
print_help err
|
16
|
+
return 0
|
17
|
+
end
|
18
|
+
|
19
|
+
if args.include?('-v') || args.include?('--version')
|
20
|
+
print_version err
|
21
|
+
return 0
|
22
|
+
end
|
23
|
+
|
24
|
+
cmd_sw_index = args.index('-c') || args.index('--cmd')
|
25
|
+
if cmd_sw_index
|
26
|
+
specs = args.take(cmd_sw_index) # specs are before switch
|
27
|
+
cmd = args.drop(cmd_sw_index+1).join ' ' # command components are after switch, join into string to pass to subshell
|
28
|
+
if cmd.empty?
|
29
|
+
err.puts "Error: No command given"
|
30
|
+
return 2
|
31
|
+
end
|
32
|
+
else
|
33
|
+
specs = args
|
34
|
+
cmd = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
results = []
|
38
|
+
specs.each do |spec|
|
39
|
+
spec = spec.strip
|
40
|
+
begin
|
41
|
+
host_spec = Hostspec.new spec
|
42
|
+
rescue Exception => e
|
43
|
+
err.puts "Error: #{e.message}"
|
44
|
+
return 3
|
45
|
+
end
|
46
|
+
if cmd.nil?
|
47
|
+
host_spec.each { |address| out.puts address }
|
48
|
+
else
|
49
|
+
env = {
|
50
|
+
'HOSTSPEC_MASK' => host_spec.mask,
|
51
|
+
'HOSTSPEC_MASKLEN' => host_spec.mask_length.to_s,
|
52
|
+
'HOSTSPEC_COUNT' => host_spec.size.to_s
|
53
|
+
}
|
54
|
+
host_spec.each_with_index do |address,index|
|
55
|
+
env['HOSTSPEC_IP'] = address
|
56
|
+
env['HOSTSPEC_INDEX'] = (index+1).to_s
|
57
|
+
# this uses posix 'sh', to use bash user's cmd should be 'bash -c "echo \$HOSTSPEC_IP"' and deal with the weird nested quoting
|
58
|
+
results << system( env, cmd )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
( results.empty? || results.all? ) ? 0 : 4
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.print_help io
|
66
|
+
io.puts "Usage: hostspec [ options ] specs... [ [ -c / --cmd ] command ]"
|
67
|
+
io.puts
|
68
|
+
io.puts "Prints all IP addresses for IP host specifications (see Specs:)."
|
69
|
+
io.puts "If specified, a command is executed for each IP address, with related values in environment variables (see Command:)."
|
70
|
+
io.puts
|
71
|
+
io.puts "Specs:"
|
72
|
+
io.puts " Nmap-style IP host specifications, multiple specs separated by spaces."
|
73
|
+
io.puts " Single host : x.x.x.x or hostname"
|
74
|
+
io.puts " Multiple hosts:"
|
75
|
+
io.puts " - by mask length : x.x.x.x/m or hostname/m"
|
76
|
+
io.puts " - by octet values : x.x.x.a,b,c"
|
77
|
+
io.puts " - by octet ranges : x.x.x.d-e"
|
78
|
+
io.puts " Octet values and ranges may be combined or applied to any/multiple octets."
|
79
|
+
io.puts " Examples:"
|
80
|
+
io.puts " hostname : localhost => 127.0.0.1"
|
81
|
+
io.puts " hostname w/mask : localhost/24 => 127.0.0.0 127.0.0.1 ... 127.0.0.254 127.0.0.255"
|
82
|
+
io.puts " address : 1.1.1.1 => 1.1.1.1"
|
83
|
+
io.puts " address w/mask : 2.2.2.1/24 => 2.2.2.0 2.2.2.1 ... 2.2.2.254 2.2.2.255"
|
84
|
+
io.puts " address w/values : 3.3.3.10,20,30 => 3.3.3.10 3.3.3.20 3.3.3.30"
|
85
|
+
io.puts " address w/ranges : 4.4.4.40-50 => 4.4.4.40 4.4.4.41 ... 4.4.4.49 4.4.4.50"
|
86
|
+
io.puts " address w/combo : 5.5.5.2,4-6,8 => 5.5.5.2 5.5.5.4 5.5.5.5 5.5.5.6 5.5.5.8"
|
87
|
+
io.puts " complex : 6.1-2,3.4-5.6 => 6.1.4.6 6.1.5.6 6.2.4.6 6.2.5.6 6.3.4.6 6.3.5.6"
|
88
|
+
io.puts
|
89
|
+
io.puts "Command:"
|
90
|
+
io.puts " A command to execute for each IP address may be specified following the command switch ( -c / --cmd )."
|
91
|
+
io.puts " The command is executed in a separate shell for each IP address."
|
92
|
+
io.puts " Environment variables are provided with values for each IP address command execution."
|
93
|
+
io.puts " Quote these variables in the command to prevent substitution by the current shell."
|
94
|
+
io.puts " $HOSTSPEC_IP : IP address"
|
95
|
+
io.puts " $HOSTSPEC_MASK : Mask (255.255.255.255 if a mask length was not specified)"
|
96
|
+
io.puts " $HOSTSPEC_MASKLEN : Mask length (32 if a mask length was not specified)"
|
97
|
+
io.puts " $HOSTSPEC_COUNT : Count of IP addresses"
|
98
|
+
io.puts " $HOSTSPEC_INDEX : Index of IP address (from 1 to Count)"
|
99
|
+
io.puts " Examples:"
|
100
|
+
io.puts " Print IP addresses and mask length with index and count:"
|
101
|
+
io.puts " hostspec 1.1.1.0/30 --cmd echo '$HOSTSPEC_INDEX' of '$HOSTSPECT_COUNT' : '$HOSTSPEC_IP/$HOSTSPEC_MASKLEN'"
|
102
|
+
io.puts " ..."
|
103
|
+
io.puts " 1 of 4 : 1.1.1.0/255.255.255.252"
|
104
|
+
io.puts " 2 of 4 : 1.1.1.1/255.255.255.252"
|
105
|
+
io.puts " 3 of 4 : 1.1.1.2/255.255.255.252"
|
106
|
+
io.puts " 4 of 4 : 1.1.1.3/255.255.255.252"
|
107
|
+
io.puts " Collect routing tables of all hosts on a network via ssh:"
|
108
|
+
io.puts " hostspec 1.1.1.1-254 --cmd 'ssh me@$HOSTSPEC_IP route -n'"
|
109
|
+
io.puts " Collect default web pages from all servers on a network via curl:"
|
110
|
+
io.puts " hostspec 1.1.1.1-254 --cmd curl -o '$HOSTSPEC_IP.html' 'http://$HOSTSPEC_IP'"
|
111
|
+
io.puts " Collect IP configuration info from multiple windows systems (run from a windows system):"
|
112
|
+
io.puts " hostspec 1.1.1.1-254 --cmd psexec '\\\\%HOSTSPEC_IP%' ipconfig /all"
|
113
|
+
io.puts " Collect IP configuration info from multiple windows systems (run from a linux system with kerberos):"
|
114
|
+
io.puts " hostspec 1.1.1.1-254 --cmd winexe --kerberos yes //$(dig -x '$HOSTSPEC_IP' +short) ipconfig /all"
|
115
|
+
io.puts " ...or any task that you would have to execute individually on multiple systems."
|
116
|
+
io.puts
|
117
|
+
io.puts "Options:"
|
118
|
+
io.puts " -h / --help : Display this helpful information"
|
119
|
+
io.puts " -v / --version : Display program version"
|
120
|
+
io.puts
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.print_version io
|
124
|
+
io.puts "hostspec version #{Iqeo::Hostspec::VERSION}"
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
data/lib/iqeo/hostspec.rb
CHANGED
@@ -1,180 +1,5 @@
|
|
1
|
-
require 'resolv'
|
2
|
-
|
3
|
-
module Iqeo
|
4
|
-
|
5
|
-
class HostspecException < Exception ; end
|
6
|
-
|
7
|
-
class Hostspec
|
8
|
-
|
9
|
-
VERSION = '0.1.0.pre1'
|
10
|
-
|
11
|
-
include Enumerable
|
12
|
-
|
13
|
-
attr_reader :string, :mask, :mask_length, :address_spec, :hostname
|
14
|
-
|
15
|
-
def initialize spec_str
|
16
|
-
@string = spec_str
|
17
|
-
raise HostspecException, 'spec cannot be empty' if spec_str.empty?
|
18
|
-
host_str, mask_str = split_on_slash spec_str
|
19
|
-
raise HostspecException, 'host cannot be empty' if host_str.empty?
|
20
|
-
parse_mask mask_str
|
21
|
-
begin
|
22
|
-
parse_address_spec host_str
|
23
|
-
rescue HostspecException
|
24
|
-
parse_hostname host_str
|
25
|
-
end
|
26
|
-
raise HostspecException, 'complex spec cannot have mask length' if @mask_specified && @address_spec.any? { |octet| octet.size > 1 }
|
27
|
-
mask_address_spec
|
28
|
-
end
|
29
|
-
|
30
|
-
def split_on_slash str
|
31
|
-
case str.count '/'
|
32
|
-
when 0 then [ str.strip, '' ]
|
33
|
-
when 1 then str.strip.split '/'
|
34
|
-
else raise 'bad format, expected 0 or 1 "/"'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def parse_mask str
|
39
|
-
if str.empty?
|
40
|
-
@mask = '255.255.255.255'
|
41
|
-
@mask_length = 32
|
42
|
-
@mask_specified = false
|
43
|
-
return
|
44
|
-
end
|
45
|
-
if match = str.match( /^\d+$/ )
|
46
|
-
@mask_length = match[0].to_i
|
47
|
-
raise "bad mask length (#{@mask_length}), expected between 0 ad 32" unless @mask_length.between? 0,32
|
48
|
-
mask_int = ((2**@mask_length)-1) << (32-@mask_length)
|
49
|
-
@mask = [24,16,8,0].collect { |n| ( mask_int & ( 255 << n ) ) >> n }.join '.'
|
50
|
-
@mask_specified = true
|
51
|
-
else
|
52
|
-
raise "bad format, expected mask length after '/'"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def mask_address_spec
|
57
|
-
@address_spec.each_with_index do |octet,index|
|
58
|
-
high_bit_position = ( index * 8 ) + 1
|
59
|
-
low_bit_position = ( index + 1 ) * 8
|
60
|
-
@address_spec[index] = case
|
61
|
-
when @mask_length >= low_bit_position then octet
|
62
|
-
when @mask_length < high_bit_position then [0..255]
|
63
|
-
else
|
64
|
-
octet_mask_length = @mask_length % 8
|
65
|
-
octet_mask = ( ( 2 ** octet_mask_length ) - 1 ) << ( 8 - octet_mask_length )
|
66
|
-
octet_mask_inverted = octet_mask ^ 255
|
67
|
-
octet_min = octet_mask & octet[0]
|
68
|
-
octet_max = octet_min | octet_mask_inverted
|
69
|
-
[octet_min..octet_max]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def parse_address_spec str
|
75
|
-
octet_strs = str.split '.'
|
76
|
-
raise HostspecException, 'bad ip, expected 4 octets' unless octet_strs.size == 4
|
77
|
-
octets = octet_strs.collect { |octet_str| parse_octet octet_str }
|
78
|
-
@address_spec = octets
|
79
|
-
end
|
80
|
-
|
81
|
-
def parse_octet str
|
82
|
-
values = str.split ','
|
83
|
-
values.collect { |value_str| parse_octet_value value_str }
|
84
|
-
end
|
85
|
-
|
86
|
-
def parse_octet_value str
|
87
|
-
# values may be dash denoted ranges, possibilities...
|
88
|
-
# n : just a number : 'n'.split '-' == ['n'] <= same = problem! 'n'.split '-', -1 == [ "n" ]
|
89
|
-
# n-m : range from n to m : 'n-m'.split '-' == ['n','m'] 'n-m'.split '-', -1 == [ "n" , "m" ]
|
90
|
-
# n- : range from n to 255 : 'n-'.split '-' == ['n'] <= same = problem! 'n-'.split '-', -1 == [ "n" , "" ]
|
91
|
-
# -m : range from 0 to m : '-m'.split '-' == ['','m'] '-m'.split '-', -1 == [ "" , "m" ]
|
92
|
-
# - : range from 0 to 255 : '-'.split '-' == [] '-'.split '-', -1 == [ "" , "" ]
|
93
|
-
numbers = str.split '-', -1 # maximize return fields to distinguish 'n' from '-m'
|
94
|
-
case numbers.size
|
95
|
-
when 1 then
|
96
|
-
check_octet_value numbers[0]
|
97
|
-
numbers[0].to_i
|
98
|
-
when 2 then
|
99
|
-
numbers[0] = '0' if numbers[0].empty?
|
100
|
-
numbers[1] = '255' if numbers[1].empty?
|
101
|
-
check_octet_value numbers[0]
|
102
|
-
check_octet_value numbers[1]
|
103
|
-
range_start = numbers[0].to_i
|
104
|
-
range_finish = numbers[1].to_i
|
105
|
-
raise HostspecException, "bad ip, reversed range in octet value: #{str}" if range_start > range_finish
|
106
|
-
range_start..range_finish
|
107
|
-
else
|
108
|
-
raise HostspecException, "bad ip, invalid octet value: #{str}"
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def check_octet_value str
|
113
|
-
match = str.match /^(25[0-5]|2[0-4]\d|[0-1]\d\d|\d\d|\d)$/
|
114
|
-
raise HostspecException, "bad ip, octet value is not a number in 0-255: #{str}" unless match
|
115
|
-
end
|
116
|
-
|
117
|
-
def parse_hostname str
|
118
|
-
@hostname = str
|
119
|
-
parse_address_spec Resolv.getaddress(str)
|
120
|
-
end
|
121
|
-
|
122
|
-
def recursively_iterate_octets octet_index = 0, address = [], &block
|
123
|
-
@address_spec[octet_index].each do |item|
|
124
|
-
if item.respond_to? :each
|
125
|
-
item.each do |value|
|
126
|
-
address.push value
|
127
|
-
octet_index == 3 ? yield( address.join '.' ) : recursively_iterate_octets( octet_index + 1, address, &block )
|
128
|
-
address.pop
|
129
|
-
end
|
130
|
-
else
|
131
|
-
address.push item
|
132
|
-
octet_index == 3 ? yield( address.join '.' ) : recursively_iterate_octets( octet_index + 1, address, &block )
|
133
|
-
address.pop
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def each_address
|
139
|
-
if block_given?
|
140
|
-
recursively_iterate_octets do |address_str|
|
141
|
-
yield address_str
|
142
|
-
end
|
143
|
-
else
|
144
|
-
return to_enum( :each_address ) { size }
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
alias_method :each, :each_address
|
149
|
-
|
150
|
-
def size
|
151
|
-
if @mask_length == 32
|
152
|
-
@address_spec.inject(1) { |oc,o| oc * o.inject(0) { |vc,v| vc + ( v.respond_to?(:each) ? v.size : 1 ) } }
|
153
|
-
else
|
154
|
-
2**(32-@mask_length)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def min
|
159
|
-
first
|
160
|
-
end
|
161
|
-
|
162
|
-
def last
|
163
|
-
address = nil
|
164
|
-
each { |addr| address = addr }
|
165
|
-
address
|
166
|
-
end
|
167
|
-
|
168
|
-
def max
|
169
|
-
last
|
170
|
-
end
|
171
|
-
|
172
|
-
def minmax
|
173
|
-
[first,last]
|
174
|
-
end
|
175
|
-
|
176
|
-
end
|
177
|
-
|
178
|
-
end
|
179
1
|
|
2
|
+
require 'iqeo/hostspec/version'
|
3
|
+
require 'iqeo/hostspec/hostspec'
|
4
|
+
require 'iqeo/hostspec/runner'
|
180
5
|
|
data/spec/iqeo/hostspec_spec.rb
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
require 'iqeo/hostspec'
|
2
|
+
include Iqeo::Hostspec
|
2
3
|
|
3
|
-
describe
|
4
|
+
describe Hostspec do
|
4
5
|
|
5
6
|
context '.new' do
|
6
7
|
|
7
8
|
it 'accepts a single argument only' do
|
8
|
-
expect {
|
9
|
-
expect {
|
10
|
-
expect {
|
9
|
+
expect { Hostspec.new }.to raise_error
|
10
|
+
expect { Hostspec.new "1.1.1.1" }.to_not raise_error
|
11
|
+
expect { Hostspec.new "1.1.1.1","2.2.2.2" }.to raise_error
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'does not require a mask length' do
|
14
|
-
expect {
|
15
|
+
expect { Hostspec.new "3.3.3.3" }.to_not raise_error
|
15
16
|
end
|
16
17
|
|
17
18
|
it 'defaults to a host mask when none specified' do
|
18
|
-
hs =
|
19
|
+
hs = Hostspec.new "4.4.4.4"
|
19
20
|
hs.mask.should eq '255.255.255.255'
|
20
21
|
hs.mask_length.should eq 32
|
21
22
|
end
|
@@ -50,27 +51,27 @@ describe Iqeo::Hostspec do
|
|
50
51
|
end
|
51
52
|
|
52
53
|
it 'is specified numerically after /' do
|
53
|
-
expect {
|
54
|
-
expect {
|
55
|
-
expect {
|
54
|
+
expect { Hostspec.new "5.5.5.5/24" }.to_not raise_error
|
55
|
+
expect { Hostspec.new "5.5.5.5/" }.to raise_error
|
56
|
+
expect { Hostspec.new "5.5.5.5/xyz" }.to raise_error
|
56
57
|
end
|
57
58
|
|
58
59
|
it 'is between 0 and 32' do
|
59
60
|
(0..32).each do |masklen|
|
60
|
-
hs =
|
61
|
+
hs = Hostspec.new "1.2.3.4/#{masklen}"
|
61
62
|
hs.mask_length.should eq masklen
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
65
66
|
it 'cannot be > 32' do
|
66
67
|
[ 33, 100, 1000 ].each do |masklen|
|
67
|
-
expect {
|
68
|
+
expect { Hostspec.new "1.2.3.4/#{masklen}" }.to raise_error
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
72
|
it 'sets mask string' do
|
72
73
|
@mask_strings.each_with_index do |str,len|
|
73
|
-
hs =
|
74
|
+
hs = Hostspec.new "1.2.3.4/#{len}"
|
74
75
|
hs.mask.should eq str
|
75
76
|
end
|
76
77
|
end
|
@@ -84,8 +85,8 @@ describe Iqeo::Hostspec do
|
|
84
85
|
end
|
85
86
|
|
86
87
|
it 'cannot be empty' do
|
87
|
-
expect {
|
88
|
-
expect {
|
88
|
+
expect { Hostspec.new '' }.to raise_error
|
89
|
+
expect { Hostspec.new '/32' }.to raise_error
|
89
90
|
end
|
90
91
|
|
91
92
|
context 'an IP address' do
|
@@ -93,7 +94,7 @@ describe Iqeo::Hostspec do
|
|
93
94
|
it 'may specify a single host without a mask length' do
|
94
95
|
@octets.each_cons(4) do |octets|
|
95
96
|
address = octets.join('.')
|
96
|
-
hs =
|
97
|
+
hs = Hostspec.new address
|
97
98
|
hs.address_spec.collect(&:first).should eq octets
|
98
99
|
end
|
99
100
|
end
|
@@ -107,7 +108,7 @@ describe Iqeo::Hostspec do
|
|
107
108
|
'1.1.1.1/0' => [[0..255],[0..255],[0..255],[0..255]]
|
108
109
|
}
|
109
110
|
slash_specs.each do |spec_str,spec_data|
|
110
|
-
hs =
|
111
|
+
hs = Hostspec.new spec_str
|
111
112
|
hs.address_spec.should eq spec_data
|
112
113
|
end
|
113
114
|
end
|
@@ -248,7 +249,7 @@ describe Iqeo::Hostspec do
|
|
248
249
|
'255.255.255.255/0' => [[0..255] ,[0..255],[0..255],[0..255]],
|
249
250
|
}
|
250
251
|
slash_specs.each do |spec_str,spec_data|
|
251
|
-
hs =
|
252
|
+
hs = Hostspec.new spec_str
|
252
253
|
hs.address_spec.should eq spec_data
|
253
254
|
end
|
254
255
|
end
|
@@ -258,16 +259,16 @@ describe Iqeo::Hostspec do
|
|
258
259
|
context 'a hostname' do
|
259
260
|
|
260
261
|
it 'is assumed when not a simple IP spec' do
|
261
|
-
|
262
|
+
Hostspec.new('localhost.iqeo.net').hostname.should eq 'localhost.iqeo.net'
|
262
263
|
end
|
263
264
|
|
264
265
|
it 'is assumed when not a complex IP spec' do
|
265
|
-
expect { hs =
|
266
|
+
expect { hs = Hostspec.new "1.2.3.100-300" }.to raise_error Resolv::ResolvError
|
266
267
|
end
|
267
268
|
|
268
269
|
it 'resolves to a host IP address' do
|
269
|
-
hs =
|
270
|
-
hs.hostname.should eq 'localhost'
|
270
|
+
hs = Hostspec.new('localhost.iqeo.net')
|
271
|
+
hs.hostname.should eq 'localhost.iqeo.net'
|
271
272
|
hs.address_spec.collect(&:first).should eq [127,0,0,1]
|
272
273
|
hs.mask.should eq '255.255.255.255'
|
273
274
|
hs.mask_length.should eq 32
|
@@ -275,7 +276,7 @@ describe Iqeo::Hostspec do
|
|
275
276
|
|
276
277
|
it 'may specify address range with a mask length' do
|
277
278
|
(0..32).each do |masklen|
|
278
|
-
hs =
|
279
|
+
hs = Hostspec.new("localhost.iqeo.net/#{masklen}")
|
279
280
|
hs.mask_length.should eq masklen
|
280
281
|
end
|
281
282
|
end
|
@@ -286,51 +287,51 @@ describe Iqeo::Hostspec do
|
|
286
287
|
|
287
288
|
it 'must not specify a mask length' do
|
288
289
|
(0..32).each do |masklen|
|
289
|
-
expect { hs =
|
290
|
+
expect { hs = Hostspec.new "10.20,22,24.30-39.40/#{masklen}" }.to raise_error
|
290
291
|
end
|
291
292
|
end
|
292
293
|
|
293
294
|
it 'may specify octet values with commas' do
|
294
|
-
hs =
|
295
|
+
hs = Hostspec.new '10,11,12.20,21,22.30,31,32.40,41,42'
|
295
296
|
hs.address_spec.should eq [[10,11,12],[20,21,22],[30,31,32],[40,41,42]]
|
296
297
|
end
|
297
298
|
|
298
299
|
context 'may specify octet value ranges with dashes' do
|
299
300
|
|
300
301
|
it 'in form "n-m"' do
|
301
|
-
hs =
|
302
|
+
hs = Hostspec.new '10-19.20-29.30-39.40-49'
|
302
303
|
hs.address_spec.should eq [[10..19],[20..29],[30..39],[40..49]]
|
303
304
|
end
|
304
305
|
|
305
306
|
it 'in form "n-"' do
|
306
|
-
hs =
|
307
|
+
hs = Hostspec.new '10-.20-.30-.40-'
|
307
308
|
hs.address_spec.should eq [[10..255],[20..255],[30..255],[40..255]]
|
308
309
|
end
|
309
310
|
|
310
311
|
it 'in form "-m"' do
|
311
|
-
hs =
|
312
|
+
hs = Hostspec.new '-19.-29.-39.-49'
|
312
313
|
hs.address_spec.should eq [[0..19],[0..29],[0..39],[0..49]]
|
313
314
|
end
|
314
315
|
|
315
316
|
it 'in form "-"' do
|
316
|
-
hs =
|
317
|
+
hs = Hostspec.new '-.-.-.-'
|
317
318
|
hs.address_spec.should eq [[0..255],[0..255],[0..255],[0..255]]
|
318
319
|
end
|
319
320
|
|
320
321
|
end
|
321
322
|
|
322
323
|
it 'may mix octet specifications with dashes and commas' do
|
323
|
-
hs =
|
324
|
+
hs = Hostspec.new '1,10,100,200.13-247.23-.-99'
|
324
325
|
hs.address_spec.should eq [[1,10,100,200],[13..247],[23..255],[0..99]]
|
325
326
|
end
|
326
327
|
|
327
328
|
it 'may combine octet specification with dashes and commas' do
|
328
|
-
hs =
|
329
|
+
hs = Hostspec.new '0,1,10,100-200,250,254,255.-50,99,200-.-33,44,55-66,77,88-.-'
|
329
330
|
hs.address_spec.should eq [[0,1,10,100..200,250,254,255],[0..50,99,200..255],[0..33,44,55..66,77,88..255],[0..255]]
|
330
331
|
end
|
331
332
|
|
332
333
|
it 'may not specifiy a reversed range' do
|
333
|
-
expect { hs =
|
334
|
+
expect { hs = Hostspec.new '1.1.1.20-10' }.to raise_error
|
334
335
|
end
|
335
336
|
|
336
337
|
end
|
@@ -374,7 +375,7 @@ describe Iqeo::Hostspec do
|
|
374
375
|
it 'a single address for host address' do
|
375
376
|
@octets.each_cons(4) do |octets|
|
376
377
|
address = octets.join('.')
|
377
|
-
hs =
|
378
|
+
hs = Hostspec.new address
|
378
379
|
address_count = 0
|
379
380
|
hs.send(method) do |address_str|
|
380
381
|
address_str.should eq address
|
@@ -385,7 +386,7 @@ describe Iqeo::Hostspec do
|
|
385
386
|
end
|
386
387
|
|
387
388
|
it 'multiple addresses for a spec with multiple octet values (commas)' do
|
388
|
-
hs =
|
389
|
+
hs = Hostspec.new @multi_spec_with_commas
|
389
390
|
address_count = 0
|
390
391
|
hs.send(method) do |address_str|
|
391
392
|
address_str.should eq @multi_expected_addresses[address_count]
|
@@ -395,7 +396,7 @@ describe Iqeo::Hostspec do
|
|
395
396
|
end
|
396
397
|
|
397
398
|
it 'multiple addresses for a spec with octet ranges (dashes)' do
|
398
|
-
hs =
|
399
|
+
hs = Hostspec.new @multi_spec_with_dashes
|
399
400
|
address_count = 0
|
400
401
|
hs.send(method) do |address_str|
|
401
402
|
address_str.should eq @multi_expected_addresses[address_count]
|
@@ -406,7 +407,7 @@ describe Iqeo::Hostspec do
|
|
406
407
|
|
407
408
|
it 'masked addresses for specs with a mask length' do
|
408
409
|
@specs_with_slash.each do |address_spec,expected_addresses|
|
409
|
-
hs =
|
410
|
+
hs = Hostspec.new address_spec
|
410
411
|
address_count = 0
|
411
412
|
hs.send(method) do |address_str|
|
412
413
|
address_str.should eq expected_addresses[address_count]
|
@@ -429,59 +430,59 @@ describe Iqeo::Hostspec do
|
|
429
430
|
context 'enumerable' do
|
430
431
|
|
431
432
|
it '.each returns an Enumerator' do
|
432
|
-
hs =
|
433
|
+
hs = Hostspec.new '10.20.30.40/24'
|
433
434
|
hs.each.class.should eq Enumerator
|
434
435
|
end
|
435
436
|
|
436
437
|
it 'responds to Enumerable methods' do
|
437
|
-
hs =
|
438
|
+
hs = Hostspec.new '10.20.30.40/24'
|
438
439
|
hs.all? { |i| i.start_with? '10' }.should be_true
|
439
440
|
hs.any? { |i| i.end_with? '255' }.should be_true
|
440
441
|
end
|
441
442
|
|
442
443
|
it 'can calculate size for simple specs' do
|
443
444
|
(0..32).each do |masklen|
|
444
|
-
hs =
|
445
|
+
hs = Hostspec.new "10.20.30.40/#{masklen}"
|
445
446
|
hs.size.should eq 2**(32-masklen)
|
446
447
|
end
|
447
448
|
end
|
448
449
|
|
449
450
|
it 'can calculate size for complex specs' do
|
450
|
-
hs =
|
451
|
+
hs = Hostspec.new @multi_spec_with_commas
|
451
452
|
hs.size.should eq @multi_expected_addresses.size
|
452
|
-
hs =
|
453
|
+
hs = Hostspec.new @multi_spec_with_dashes
|
453
454
|
hs.size.should eq @multi_expected_addresses.size
|
454
455
|
end
|
455
456
|
|
456
457
|
it 'Enumerator can make use of size' do
|
457
|
-
hs =
|
458
|
+
hs = Hostspec.new '1.1.1.1-10'
|
458
459
|
hs.size.should eq 10
|
459
460
|
enumerator = hs.each
|
460
461
|
enumerator.size.should eq 10
|
461
462
|
end
|
462
463
|
|
463
464
|
it 'has first (from enumerable)' do
|
464
|
-
hs =
|
465
|
+
hs = Hostspec.new '1.1.2-10.20-100'
|
465
466
|
hs.first.should eq '1.1.2.20'
|
466
467
|
end
|
467
468
|
|
468
469
|
it 'has last' do
|
469
|
-
hs =
|
470
|
+
hs = Hostspec.new '1.1.2-10.20-100'
|
470
471
|
hs.last.should eq '1.1.10.100'
|
471
472
|
end
|
472
473
|
|
473
474
|
it 'has min equals first' do
|
474
|
-
hs =
|
475
|
+
hs = Hostspec.new '1.1.2-10.20-100'
|
475
476
|
hs.min.should eq '1.1.2.20'
|
476
477
|
end
|
477
478
|
|
478
479
|
it 'has max equals last' do
|
479
|
-
hs =
|
480
|
+
hs = Hostspec.new '1.1.2-10.20-100'
|
480
481
|
hs.max.should eq '1.1.10.100'
|
481
482
|
end
|
482
483
|
|
483
484
|
it 'has minmax' do
|
484
|
-
hs =
|
485
|
+
hs = Hostspec.new '1.1.2-10.20-100'
|
485
486
|
hs.minmax.should eq ['1.1.2.20','1.1.10.100']
|
486
487
|
end
|
487
488
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
load './bin/hostspec'
|
2
|
+
include Iqeo::Hostspec
|
2
3
|
|
3
4
|
require 'stringio'
|
4
5
|
|
@@ -15,13 +16,13 @@ describe Runner do
|
|
15
16
|
Runner.run( ['10.20.30.40'], out: StringIO.new, err: StringIO.new ).should eq 0
|
16
17
|
Runner.run( ['10.20.30.40/24'], out: StringIO.new, err: StringIO.new ).should eq 0
|
17
18
|
Runner.run( ['10,11.20-29.30,31-38,39.40'], out: StringIO.new, err: StringIO.new ).should eq 0
|
18
|
-
Runner.run( ['localhost'], out: StringIO.new, err: StringIO.new ).should eq 0
|
19
|
+
Runner.run( ['localhost.iqeo.net'], out: StringIO.new, err: StringIO.new ).should eq 0
|
19
20
|
end
|
20
21
|
|
21
22
|
it 'accepts multiple host specs for arguments with no switches' do
|
22
23
|
Runner.run( ['10.20.30.40/24','11.22.33.44'], out: StringIO.new, err: StringIO.new ).should eq 0
|
23
24
|
Runner.run( ['10,11.20-29.30,31-38,39.40','11.22.33.44/28'], out: StringIO.new, err: StringIO.new ).should eq 0
|
24
|
-
Runner.run( ['localhost','1.2.3.4/26','11.22.33.44-55'], out: StringIO.new, err: StringIO.new ).should eq 0
|
25
|
+
Runner.run( ['localhost.iqeo.net','1.2.3.4/26','11.22.33.44-55'], out: StringIO.new, err: StringIO.new ).should eq 0
|
25
26
|
end
|
26
27
|
|
27
28
|
it 'expects valid single host spec or prints error' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iqeo-hostspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerard Fowley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -33,6 +33,7 @@ extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
35
|
- ".gitignore"
|
36
|
+
- ".travis.yml"
|
36
37
|
- Gemfile
|
37
38
|
- Guardfile
|
38
39
|
- LICENSE.txt
|
@@ -41,8 +42,11 @@ files:
|
|
41
42
|
- bin/hostspec
|
42
43
|
- iqeo-hostspec.gemspec
|
43
44
|
- lib/iqeo/hostspec.rb
|
44
|
-
-
|
45
|
+
- lib/iqeo/hostspec/hostspec.rb
|
46
|
+
- lib/iqeo/hostspec/runner.rb
|
47
|
+
- lib/iqeo/hostspec/version.rb
|
45
48
|
- spec/iqeo/hostspec_spec.rb
|
49
|
+
- spec/iqeo/runner_spec.rb
|
46
50
|
- tmux
|
47
51
|
homepage: ''
|
48
52
|
licenses:
|
@@ -69,5 +73,5 @@ signing_key:
|
|
69
73
|
specification_version: 4
|
70
74
|
summary: Write a gem summary
|
71
75
|
test_files:
|
72
|
-
- spec/hostspec_runner_spec.rb
|
73
76
|
- spec/iqeo/hostspec_spec.rb
|
77
|
+
- spec/iqeo/runner_spec.rb
|