ipadmin 0.3.0 → 0.4.0

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/lib/tree.rb CHANGED
@@ -1,9 +1,3 @@
1
- =begin rdoc
2
- Copyright (c) 2006 Dustin Spinhirne -
3
- Licensed under the same terms as Ruby, No Warranty is provided.
4
- =end
5
-
6
-
7
1
  module IPAdmin
8
2
  class Tree
9
3
 
@@ -14,11 +8,19 @@ class Tree
14
8
  # @version
15
9
 
16
10
  #==============================================================================#
17
- # attr_reader / attr_writer
11
+ # initialize()
18
12
  #==============================================================================#
19
13
 
20
- # IP version for this tree (4 or 6)
21
- attr_reader :version
14
+ # - Arguments:
15
+ # * none
16
+ #
17
+ # Examples:
18
+ # tree = IPAdmin::Tree.new()
19
+ #
20
+ def initialize(options=nil)
21
+ # root of our ordered IP tree
22
+ @root = NetStruct.new(nil, nil, [])
23
+ end
22
24
 
23
25
  #======================================#
24
26
  #
@@ -26,42 +28,68 @@ class Tree
26
28
 
27
29
 
28
30
  #==============================================================================#
29
- # initialize()
31
+ # add!()
30
32
  #==============================================================================#
31
33
 
34
+ # Add a CIDR address or IPAdmin::CIDR object to the tree.
35
+ #
32
36
  # - Arguments:
33
- # * Hash with the following fields:
34
- # - :Version -- IP version - Integer
37
+ # * CIDR address or IPAdmin::CIDR object
35
38
  #
36
- # Example:
37
- # table = IPAdmin::CIDRTable.new(:Version => 4)
39
+ # - Returns:
40
+ # * nothing
38
41
  #
39
- def initialize(options)
40
- if (!options.kind_of? Hash)
41
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
42
- end
43
-
44
- if ( options.has_key?(:Version) )
45
- @version = options[:Version]
42
+ # Examples:
43
+ # tree.add!('192.168.1.0/24')
44
+ #
45
+ def add!(new)
46
+ duplicate = false
46
47
 
47
- if ( @version != 4 && @version != 6 )
48
- raise "IP version should be either 4 or 6."
48
+ # validate object
49
+ if ( !new.kind_of?(IPAdmin::CIDR) )
50
+ begin
51
+ cidr = IPAdmin::CIDR.new(:CIDR => new)
52
+ rescue Exception => error
53
+ raise ArgumentError, "Provided argument raised the following " +
54
+ "errors: #{error}"
49
55
  end
50
56
  else
51
- raise ArgumentError, "Missing argument: Version."
57
+ cidr = IPAdmin::CIDR.new(:PackedIP => new.packed_ip,
58
+ :PackedNetmask => new.packed_netmask,
59
+ :Version => new.version,
60
+ :Tag => new.tag)
61
+ end
62
+
63
+ version = cidr.version
64
+ new_entry = NetStruct.new(cidr, nil, [])
65
+
66
+ # find cidr's parent
67
+ new_entry.parent = find_parent(cidr, @root)
68
+
69
+ # check parent for subnets of cidr
70
+ new_entry.parent.children.each do |old_entry|
71
+ if (old_entry.cidr.version == version)
72
+ comp = IPAdmin.compare(cidr,old_entry.cidr)
73
+ if (comp && comp[0] == cidr)
74
+ old_entry.parent = new_entry
75
+ new_entry.children.push(old_entry)
76
+ elsif (comp && comp == 1)
77
+ duplicate = true
78
+ break
79
+ end
80
+ end
52
81
  end
53
82
 
54
- if (@version == 4)
55
- @max_bits = 32
56
- @all_f = 2**@max_bits - 1
57
- else
58
- @max_bits = 128
59
- @all_f = 2**@max_bits - 1
60
- end
83
+ if (!duplicate)
84
+ new_entry.children.each do |old_entry|
85
+ new_entry.parent.children.delete(old_entry)
86
+ end
61
87
 
62
- # root of our ordered IP tree
63
- @root = []
88
+ # add new object as an ordered entry to parent.children
89
+ add_to_parent(new_entry,new_entry.parent)
90
+ end
64
91
 
92
+ return()
65
93
  end
66
94
 
67
95
  #======================================#
@@ -70,68 +98,118 @@ class Tree
70
98
 
71
99
 
72
100
  #==============================================================================#
73
- # add()
101
+ # ancestors()
74
102
  #==============================================================================#
75
103
 
76
- # Add an IPAdmin::CIDR object to the tree.
104
+ # Returns all the ancestors of the provided CIDR addresses.
77
105
  #
78
106
  # - Arguments:
79
- # * CIDR object
107
+ # * CIDR address or IPAdmin::CIDR object
80
108
  #
81
109
  # - Returns:
82
- # * nothing
110
+ # * Array of IPAdmin::CIDR objects
83
111
  #
84
- # Example:
85
- # tree.add(cidr)
112
+ # Examples:
113
+ # puts tree.ancestors('192.168.1.0/27')
86
114
  #
87
- def add(object)
88
-
115
+ def ancestors(cidr)
89
116
  # validate object
90
- if ( !object.kind_of?(IPAdmin::CIDR) )
91
- raise ArgumentError, "IPAdmin::CIDR object " +
92
- "required but #{object.class} provided."
117
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
118
+ begin
119
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
120
+ rescue Exception => error
121
+ raise ArgumentError, "Provided argument raised the following " +
122
+ "errors: #{error}"
123
+ end
93
124
  end
94
-
95
- if ( object.version != @version )
96
- raise "IP version #{object.version} is incompatible with " +
97
- "Tree IP version #{@version}."
125
+
126
+ list = []
127
+ parent = find_parent(cidr,@root)
128
+
129
+ until (parent == @root)
130
+ list.push(parent.cidr)
131
+ parent = parent.parent
98
132
  end
99
133
 
100
- net_struct = IPAdmin.create_net_struct(object)
134
+ return(list)
135
+ end
101
136
 
102
- # search tree for it's home branch
103
- search_results = find_home(net_struct, @root)
104
- home_struct = search_results[0] if (search_results)
105
- home_branch = home_struct.subnets if (search_results)
106
- home_branch = @root if (!home_branch)
137
+ #======================================#
138
+ #
139
+ #======================================#
107
140
 
108
- # make sure we dont have a duplicate
109
- if (home_struct)
110
- if ( (net_struct.network == home_struct.network) &&
111
- (net_struct.netmask == home_struct.netmask) )
112
- raise "Attempted to add #{object.desc()} multiple times."
113
- end
114
- end
115
141
 
116
- # now that we have our branch location, check that other entries
117
- # of this branch are not subnets of our new object. if they are
118
- # then make them a branch of our new object
119
- home_branch.each do |entry|
120
- is_contained = contains(net_struct, entry)
142
+ #==============================================================================#
143
+ # children()
144
+ #==============================================================================#
121
145
 
122
- if (is_contained)
123
- net_struct.subnets.push(entry)
146
+ # Returns all the immediate children of the provided CIDR addresses.
147
+ #
148
+ # - Arguments:
149
+ # * CIDR address or IPAdmin::CIDR object
150
+ #
151
+ # - Returns:
152
+ # * Array of IPAdmin::CIDR objects
153
+ #
154
+ # Examples:
155
+ # puts tree.children('192.168.1.0/24')
156
+ #
157
+ def children(cidr)
158
+ # validate object
159
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
160
+ begin
161
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
162
+ rescue Exception => error
163
+ raise ArgumentError, "Provided argument raised the following " +
164
+ "errors: #{error}"
124
165
  end
125
166
  end
126
167
 
127
- net_struct.subnets.each do |entry|
128
- home_branch.delete(entry)
168
+ list = []
169
+ find_me(cidr).children.each do |child|
170
+ list.push(child.cidr)
171
+ end
172
+
173
+ return(list)
174
+ end
175
+
176
+ #======================================#
177
+ #
178
+ #======================================#
179
+
180
+
181
+ #==============================================================================#
182
+ # descendants
183
+ #==============================================================================#
184
+
185
+ # Return all descendants of the provided CIDR address.
186
+ #
187
+ # - Arguments:
188
+ # * CIDR address or IPAdmin::CIDR object
189
+ #
190
+ # - Returns:
191
+ # * Array of IPAdmin::CIDR objects
192
+ #
193
+ # Examples:
194
+ # descendants = tree.descendants('192.168.1.0/24')
195
+ #
196
+ def descendants(cidr)
197
+ list = []
198
+
199
+ # validate object
200
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
201
+ begin
202
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
203
+ rescue Exception => error
204
+ raise ArgumentError, "Provided argument raised the following " +
205
+ "errors: #{error}"
206
+ end
129
207
  end
130
208
 
131
- # add new object as an ordered entry to home_branch
132
- add_to_branch(net_struct,home_branch)
209
+ me = find_me(cidr)
210
+ dump_children(me).each {|x| list.push(x[:NetStruct].cidr)} if (me)
133
211
 
134
- return(nil)
212
+ return(list)
135
213
  end
136
214
 
137
215
  #======================================#
@@ -140,39 +218,46 @@ class Tree
140
218
 
141
219
 
142
220
  #==============================================================================#
143
- # collapse()
221
+ # delete!()
144
222
  #==============================================================================#
145
223
 
146
- # Collapse (supernet) all subnets of the tree,
147
- # and return a new tree. The supernetting of subnets will only occur in
148
- # situations where the newly created supernet will not result in the
149
- # 'creation' of additional space. For example the following blocks
150
- # (192.168.0.0/24, 192.168.1.0/24, and 192.168.2.0/24) would merge into
151
- # 192.168.0.0/23 and 192.168.2.0/24 rather than into 192.168.0.0/22.
224
+ # Remove the provided CIDR address from the tree.
152
225
  #
153
226
  # - Arguments:
154
- # * none
227
+ # * CIDR address of IPAdmin::CIDR object
155
228
  #
156
229
  # - Returns:
157
- # * Tree object
158
- #
159
- # Example:
160
- # new_tree = tree.collapse()
161
- #
162
- def collapse()
163
- tree = IPAdmin::Tree.new(:Version => @version)
164
- branch = IPAdmin.merge(:List => @root, :NetStruct => 1)
165
- dumped = dump_branch(branch)
166
- dumped.each do |entry|
167
- net_struct = entry[:NetStruct]
168
- network = IPAdmin.unpack_ip_addr(:Integer => net_struct.network,
169
- :Version => @version)
170
- netmask = IPAdmin.unpack_ip_netmask(:Integer => net_struct.netmask,
171
- :Version => @version)
172
- cidr = IPAdmin::CIDR.new(:CIDR => "#{network}/#{netmask}")
173
- tree.add(cidr)
230
+ # * true on success or false on fail
231
+ #
232
+ # Examples:
233
+ # did_remove = tree.remove!('192.168.1.0/24')
234
+ #
235
+ def delete!(cidr)
236
+ removed = false
237
+
238
+ # validate object
239
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
240
+ begin
241
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
242
+ rescue Exception => error
243
+ raise ArgumentError, "Provided argument raised the following " +
244
+ "errors: #{error}"
174
245
  end
175
- return(tree)
246
+ end
247
+
248
+ # find matching
249
+ me = find_me(cidr)
250
+
251
+ # remove
252
+ if (me)
253
+ parent = me.parent
254
+ children = me.children
255
+ parent.children.delete(me)
256
+ children.each {|x| add_to_parent(x,parent)}
257
+ removed = true
258
+ end
259
+
260
+ return(removed)
176
261
  end
177
262
 
178
263
  #======================================#
@@ -191,20 +276,19 @@ class Tree
191
276
  #
192
277
  # - Returns:
193
278
  # * ordered array of hashes with the following fields:
194
- # - :Object => CIDR object
279
+ # - :CIDR => IPAdmin::CIDR object
195
280
  # - :Depth => (depth level in tree)
196
- # Example:
281
+ # Examples:
197
282
  # dumped = tree.dump()
198
283
  #
199
284
  def dump()
200
285
  list = []
201
- dumped = dump_branch(@root)
286
+ dumped = dump_children(@root)
202
287
 
203
288
  dumped.each do |entry|
204
289
  depth = entry[:Depth]
205
290
  net_struct = entry[:NetStruct]
206
- object = net_struct.object
207
- list.push({:Depth => depth, :Object => object})
291
+ list.push({:Depth => depth, :CIDR => net_struct.cidr})
208
292
  end
209
293
 
210
294
  return(list)
@@ -219,43 +303,31 @@ class Tree
219
303
  # exists?()
220
304
  #==============================================================================#
221
305
 
222
- # Has an IPAdmin::CIDR object already been added to the tree?
306
+ # Has a CIDR address already been added to the tree?
223
307
  #
224
308
  # - Arguments:
225
- # * CIDR object
309
+ # * CIDR address or IPAdmin::CIDR object
226
310
  #
227
311
  # - Returns:
228
312
  # * true or false
229
313
  #
230
- # Example:
231
- # added = tree.exists?(cidr)
314
+ # Examples:
315
+ # added = tree.exists?('192.168.1.0/24')
232
316
  #
233
- def exists?(object)
317
+ def exists?(cidr)
318
+ found = false
234
319
 
235
320
  # validate object
236
- if ( !object.kind_of?(IPAdmin::CIDR) )
237
- raise ArgumentError, "IPAdmin::CIDR object " +
238
- "required but #{object.class} provided."
239
- end
240
-
241
- if ( !object.version == @version )
242
- raise "IP version #{object.version} is incompatible with " +
243
- "Tree IP version #{@version}."
244
- end
245
-
246
- net_struct = IPAdmin.create_net_struct(object)
247
- home_struct,home_branch = find_home(net_struct, @root)
248
-
249
- # check for match
250
- found = false
251
- if (home_struct)
252
- if ( (net_struct.network == home_struct.network) &&
253
- (net_struct.netmask == home_struct.netmask) )
254
- found = true
321
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
322
+ begin
323
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
324
+ rescue Exception => error
325
+ raise ArgumentError, "Provided argument raised the following " +
326
+ "errors: #{error}"
255
327
  end
256
328
  end
257
329
 
258
-
330
+ found = true if (find_me(cidr))
259
331
  return(found)
260
332
  end
261
333
 
@@ -265,37 +337,76 @@ class Tree
265
337
 
266
338
 
267
339
  #==============================================================================#
268
- # find()
340
+ # fill_in!()
269
341
  #==============================================================================#
270
342
 
271
- # Find the longest matching branch of our tree to which an
272
- # IPAdmin::CIDR object belongs. Useful for performing 'routing table' lookups.
343
+ # Fill in the missing subnets of a particular CIDR.
273
344
  #
274
345
  # - Arguments:
275
- # * CIDR object
346
+ # * CIDR address or IPAdmin::CIDR object
276
347
  #
277
348
  # - Returns:
278
- # * CIDR object, or nil
349
+ # * true or false
279
350
  #
280
- # Example:
281
- # longest_match = tree.find(ip)
351
+ # Examples:
352
+ # success = tree.fill_in!('192.168.1.0/24')
282
353
  #
283
- def find(object)
284
- if ( !object.kind_of?(IPAdmin::CIDR) )
285
- raise ArgumentError, "IPAdmin::CIDR object " +
286
- "required but #{object.class} provided."
354
+ def fill_in!(cidr)
355
+ filled = false
356
+
357
+ # validate object
358
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
359
+ begin
360
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
361
+ rescue Exception => error
362
+ raise ArgumentError, "Provided argument raised the following " +
363
+ "errors: #{error}"
364
+ end
287
365
  end
288
366
 
289
- if ( object.version != @version )
290
- raise "IP version #{object.version} is incompatible with " +
291
- "Tree IP version #{@version}."
367
+ me = find_me(cidr)
368
+ if (me && me.children.length != 0)
369
+ list = []
370
+ me.children.each {|x| list.push(x.cidr)}
371
+ me.cidr.fill_in(:List => list, :Objectify => true).each do |cidr|
372
+ self.add!(cidr)
373
+ end
374
+ filled = true
292
375
  end
376
+ return(filled)
377
+ end
293
378
 
294
- net_struct = IPAdmin.create_net_struct(object)
295
- home_struct,home_branch = find_home(net_struct, @root)
296
- found_obj = home_struct.object if (home_struct)
379
+ #======================================#
380
+ #
381
+ #======================================#
297
382
 
298
- return(found_obj)
383
+
384
+ #==============================================================================#
385
+ # find()
386
+ #==============================================================================#
387
+
388
+ # Find and return a CIDR from within the tree.
389
+ #
390
+ # - Arguments:
391
+ # * CIDR address or IPAdmin::CIDR object
392
+ #
393
+ # - Returns:
394
+ # * IPAdmin::CIDR object, or nil
395
+ #
396
+ # Examples:
397
+ # cidr = tree.find('192.168.1.0/24')
398
+ #
399
+ def find(cidr)
400
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
401
+ begin
402
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
403
+ rescue Exception => error
404
+ raise ArgumentError, "Provided argument raised the following " +
405
+ "errors: #{error}"
406
+ end
407
+ end
408
+
409
+ return(find_me(cidr).cidr)
299
410
  end
300
411
 
301
412
  #======================================#
@@ -307,86 +418,110 @@ class Tree
307
418
  # find_space()
308
419
  #==============================================================================#
309
420
 
310
- # Find Tree entries that can hold a subnet of size X. Returns only the smallest
311
- # matching subnets of each supernet. If neither IPCount or Subnet or provided,
312
- # then method returns all entries that can fit a single IP.
421
+ # Find subnets that are of at least size X. Only subnets that are not themselves
422
+ # subnetted will be returned.
313
423
  #
314
424
  # - Arguments:
315
- # * Hash with the following fields:
316
- # - :IPCount -- Minimum number of IP's that should fit within subnet - Integer (optional)
317
- # - :Limit -- Maximum entries to return - Integer (optional)
318
- # - :Subnet -- Netmask (in bits) of space to find - Integer (optional)
425
+ # * Minimum subnet size in bits, or a Hash with the following fields:
426
+ # - :Subnet - minimum subnet size in bits for returned subnets
427
+ # - :IPCount - minimum IP count per subnet required for returned subnets
428
+ # - :Version - restrict results to IPvX
319
429
  #
320
430
  # - Returns:
321
- # * Array of CIDR objects
431
+ # * Array of IPAdmin::CIDR objects
322
432
  #
323
433
  # - Notes:
324
- # * :Subnet always takes precedence over :IPCount.
434
+ # * :Subnet takes precedence over :IPCount
325
435
  #
326
- # Example:
327
- # list = tree.find_space(:Subnet => 27)
328
- # list = tree.find_space(:IPCount => 33, :Limit => 2)
436
+ # Examples:
437
+ # subnets = tree.find_space(:IPCount => 16)
329
438
  #
330
439
  def find_space(options)
331
- limit = nil
332
- list = []
333
-
334
- # validate options
335
- if (!options.kind_of? Hash)
336
- raise ArgumentError, "Expected Hash, but #{options.class} provided."
337
- end
440
+ version = nil
441
+ if (options.kind_of? Integer)
442
+ bits4 = options
443
+ bits6 = options
444
+ elsif (options.kind_of? Hash)
445
+ if (options.has_key?(:Version))
446
+ version = options[:Version]
447
+ raise "IP version should be 4 or 6, but was #{version}." if (version != 4 && version !=6)
448
+ end
338
449
 
339
- if ( options.has_key?(:Limit) )
340
- limit = options[:Limit]
341
- end
342
-
343
- if ( options.has_key?(:IPCount) )
344
- subnet_size = IPAdmin.minimum_size(:IPCount =>options[:IPCount],
345
- :Version => @version)
346
- end
347
-
348
- if ( options.has_key?(:Subnet) )
349
- subnet_size = options[:Subnet]
450
+ if (options.has_key?(:Subnet))
451
+ bits4 = options[:Subnet]
452
+ bits6 = options[:Subnet]
453
+ elsif(options.has_key?(:IPCount))
454
+ bits4 = IPAdmin.minimum_size(:IPCount => options[:IPCount], :Version => 4)
455
+ bits6 = IPAdmin.minimum_size(:IPCount => options[:IPCount], :Version => 6)
456
+ else
457
+ raise "Missing arguments: :Subnet/:IPCount"
458
+ end
459
+ else
460
+ raise "Integer or Hash expected, but #{options.class} provided."
350
461
  end
351
462
 
352
- # set subnet_size if not already
353
- if (!subnet_size)
354
- if (@version == 4)
355
- subnet_size = 32
463
+ list4 = []
464
+ list6 = []
465
+ dump_children(@root).each do |entry|
466
+ netstruct = entry[:NetStruct]
467
+ if (netstruct.cidr.version == 4)
468
+ if ( (netstruct.children.length == 0) && (netstruct.cidr.bits <= bits4) )
469
+ list4.push(netstruct.cidr)
470
+ end
356
471
  else
357
- subnet_size = 128
472
+ if ( (netstruct.children.length == 0) && (netstruct.cidr.bits <= bits6) )
473
+ list6.push(netstruct.cidr)
474
+ end
358
475
  end
359
476
  end
360
477
 
361
- # check that subnet_size is a valid size
362
- if ( (subnet_size < 1) || (subnet_size > @max_bits) )
363
- raise "#{subnet_size} is out of range for an " +
364
- "IP version #{@version} Tree."
478
+ list = []
479
+ if (!version)
480
+ list.concat(list4)
481
+ list.concat(list6)
482
+ else
483
+ list.concat(list4) if (version == 4)
484
+ list.concat(list6) if (version == 6)
365
485
  end
366
486
 
367
- # search
368
- search_list = dump_branch(@root)
369
- search_list.each do |entry|
370
- depth = entry[:Depth]
371
- net_struct = entry[:NetStruct]
487
+ return(list)
488
+ end
372
489
 
373
- # we only want leaf nodes of type IPAdmin::CIDR
374
- if ( (net_struct.subnets.length == 0) &&
375
- (net_struct.object.kind_of?(IPAdmin::CIDR)) )
490
+ #======================================#
491
+ #
492
+ #======================================#
376
493
 
377
- if (subnet_size >= net_struct.object.bits)
378
- list.push(net_struct.object)
379
- end
380
- end
381
494
 
382
- if (limit && (list.length >= limit) )
383
- break
384
- end
495
+ #==============================================================================#
496
+ # longest_match()
497
+ #==============================================================================#
385
498
 
499
+ # Find the longest matching branch of our tree to which a
500
+ # CIDR address belongs. Useful for performing 'routing table' style lookups.
501
+ #
502
+ # - Arguments:
503
+ # * CIDR address or IPAdmin::CIDR object
504
+ #
505
+ # - Returns:
506
+ # * IPAdmin::CIDR object, or nil
507
+ #
508
+ # Examples:
509
+ # longest_match = tree.longest_match('192.168.1.1')
510
+ #
511
+ def longest_match(cidr)
512
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
513
+ begin
514
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
515
+ rescue Exception => error
516
+ raise ArgumentError, "Provided argument raised the following " +
517
+ "errors: #{error}"
518
+ end
386
519
  end
520
+
521
+ found = find_me(cidr)
522
+ found = find_parent(cidr, @root) if !found
387
523
 
388
- return(list)
389
-
524
+ return(found.cidr)
390
525
  end
391
526
 
392
527
  #======================================#
@@ -395,43 +530,84 @@ class Tree
395
530
 
396
531
 
397
532
  #==============================================================================#
398
- # prune()
533
+ # merge_subnets!()
399
534
  #==============================================================================#
400
535
 
401
- # Remove an IPAdmin::CIDR object, and all child
402
- # objects from the tree.
536
+ # Merge (summarize) all subnets of the provided CIDR address.
403
537
  #
404
538
  # - Arguments:
405
- # * CIDR object
539
+ # * CIDR address or IPAdmin::CIDR object
406
540
  #
407
541
  # - Returns:
408
- # * true or false
542
+ # * true on success or false on fail
409
543
  #
410
- # Example:
411
- # did_prune = tree.prune(ip)
544
+ # Examples:
545
+ # tree.merge_subnets!('192.168.1.0/24')
412
546
  #
413
- def prune(object)
414
- if ( !object.kind_of?(IPAdmin::CIDR) )
415
- raise ArgumentError, "IPAdmin::CIDR object " +
416
- "required but #{object.class} provided."
547
+ def merge_subnets!(cidr)
548
+ merged = false
549
+
550
+ # validate object
551
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
552
+ begin
553
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
554
+ rescue Exception => error
555
+ raise ArgumentError, "Provided argument raised the following " +
556
+ "errors: #{error}"
557
+ end
417
558
  end
418
559
 
419
- if (object.version != @version )
420
- raise "IP version #{object.version} is incompatible with " +
421
- "Tree IP version #{@version}."
560
+ me = find_me(cidr)
561
+
562
+ if (me)
563
+ to_merge = []
564
+ me.children.each {|x| to_merge.push(x.cidr)}
565
+ to_merge.each {|x| delete!(x)}
566
+
567
+ IPAdmin.merge(to_merge).each {|x| add!(x)}
568
+ merged = true
422
569
  end
423
570
 
424
- net_struct = IPAdmin.create_net_struct(object)
425
- home_struct,home_branch = find_home(net_struct, @root)
571
+ return(merged)
572
+ end
426
573
 
427
- if(!home_struct)
428
- raise "#{object.desc} could not be found."
574
+ #======================================#
575
+ #
576
+ #======================================#
577
+
578
+
579
+ #==============================================================================#
580
+ # prune!()
581
+ #==============================================================================#
582
+
583
+ # Remove all subnets of the provided CIDR address.
584
+ #
585
+ # - Arguments:
586
+ # * CIDR address or IPAdmin::CIDR object
587
+ #
588
+ # - Returns:
589
+ # * true on success or false on fail
590
+ #
591
+ # Examples:
592
+ # did_prune = tree.prune!('192.168.1.0/24')
593
+ #
594
+ def prune!(cidr)
595
+ pruned = false
596
+
597
+ # validate object
598
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
599
+ begin
600
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
601
+ rescue Exception => error
602
+ raise ArgumentError, "Provided argument raised the following " +
603
+ "errors: #{error}"
604
+ end
429
605
  end
606
+
607
+ me = find_me(cidr)
430
608
 
431
- # remove if home_struct.object = object
432
- pruned = false
433
- if (home_struct.object = object)
434
- home_branch.delete(home_struct)
609
+ if (me)
610
+ me.children.clear
435
611
  pruned = true
436
612
  end
437
613
 
@@ -444,57 +620,131 @@ class Tree
444
620
 
445
621
 
446
622
  #==============================================================================#
447
- # remove()
623
+ # remove!()
448
624
  #==============================================================================#
449
625
 
450
- # Remove a single CIDR object from the tree.
626
+ # Remove the provided CIDR address, and all of its subnets from the tree.
451
627
  #
452
628
  # - Arguments:
453
- # * CIDR object
629
+ # * CIDR address of IPAdmin::CIDR object
454
630
  #
455
631
  # - Returns:
456
- # * true or false
632
+ # * true on success or false on fail
457
633
  #
458
- # Example:
459
- # did_remove = tree.remove(ip)
634
+ # Examples:
635
+ # did_remove = tree.remove!('192.168.1.0/24')
460
636
  #
461
- def remove(object)
462
- if ( !object.kind_of?(IPAdmin::CIDR) )
463
- raise ArgumentError, "IPAdmin::CIDR object " +
464
- "required but #{object.class} provided."
637
+ def remove!(cidr)
638
+ removed = false
639
+ found = nil
640
+
641
+ # validate object
642
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
643
+ begin
644
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
645
+ rescue Exception => error
646
+ raise ArgumentError, "Provided argument raised the following " +
647
+ "errors: #{error}"
648
+ end
465
649
  end
466
650
 
467
- if (object.version != @version )
468
- raise "IP version #{object.version} is incompatible with " +
469
- "Tree IP version #{@version}."
651
+ me = find_me(cidr)
652
+
653
+ if (me)
654
+ parent = me.parent
655
+ parent.children.delete(me)
656
+ removed = true
470
657
  end
658
+
659
+ return(removed)
660
+ end
471
661
 
472
- net_struct = IPAdmin.create_net_struct(object)
473
- home_struct,home_branch = find_home(net_struct, @root)
662
+ #======================================#
663
+ #
664
+ #======================================#
474
665
 
475
- # remove if home_struct.object = object
476
- removed = false
477
- if (home_struct.object = object)
478
-
479
- # if we have children subnets, move them up one level
480
- if (home_struct.subnets.length > 0)
481
- home_struct.subnets.each do |entry|
482
- index = 0
483
- home_branch.each do
484
- if(entry.network < (home_branch[index]).network)
485
- break
486
- end
487
- index += 1
488
- end
489
- home_branch.insert(index, entry)
490
- end
666
+
667
+ #==============================================================================#
668
+ # resize!()
669
+ #==============================================================================#
670
+
671
+ # Resize the provided CIDR address.
672
+ #
673
+ # - Arguments:
674
+ # * CIDR address or IPAdmin::CIDR object
675
+ # * Integer representing the bits of the new netmask
676
+ #
677
+ # - Returns:
678
+ # * true on success or false on fail
679
+ #
680
+ # Examples:
681
+ # resized = tree.resize!('192.168.1.0/24', 23)
682
+ #
683
+ def resize!(cidr,bits)
684
+ resized = false
685
+
686
+ # validate cidr
687
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
688
+ begin
689
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
690
+ rescue Exception => error
691
+ raise ArgumentError, "Provided argument raised the following " +
692
+ "errors: #{error}"
491
693
  end
694
+ end
492
695
 
493
- home_branch.delete(home_struct)
494
- removed = true
696
+ me = find_me(cidr)
697
+
698
+ if (me)
699
+ new = me.cidr.resize(bits)
700
+ delete!(me.cidr)
701
+ add!(new)
702
+ resized = true
495
703
  end
704
+
705
+ return(resized)
706
+ end
496
707
 
497
- return(removed)
708
+ #======================================#
709
+ #
710
+ #======================================#
711
+
712
+
713
+ #==============================================================================#
714
+ # root()
715
+ #==============================================================================#
716
+
717
+ # Returns the root of the provided CIDR address.
718
+ #
719
+ # - Arguments:
720
+ # * CIDR address or IPAdmin::CIDR object
721
+ #
722
+ # - Returns:
723
+ # * IPAdmin::CIDR object
724
+ #
725
+ # Examples:
726
+ # puts tree.root('192.168.1.32/27')
727
+ #
728
+ def root(cidr)
729
+ # validate object
730
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
731
+ begin
732
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
733
+ rescue Exception => error
734
+ raise ArgumentError, "Provided argument raised the following " +
735
+ "errors: #{error}"
736
+ end
737
+ end
738
+
739
+ parent = find_parent(cidr,@root)
740
+
741
+ if (parent && parent != @root)
742
+ until (parent.parent == @root)
743
+ parent = parent.parent
744
+ end
745
+ end
746
+
747
+ return(parent.cidr)
498
748
  end
499
749
 
500
750
  #======================================#
@@ -514,21 +764,16 @@ class Tree
514
764
  # - Returns:
515
765
  # * String representing the contents of the tree
516
766
  #
517
- # Example:
767
+ # Examples:
518
768
  # puts tree.show()
519
769
  #
520
770
  def show()
521
771
  printed = ""
522
- list = dump_branch(@root)
772
+ list = dump_children(@root)
523
773
 
524
774
  list.each do |entry|
525
- net_struct = entry[:NetStruct]
775
+ cidr = entry[:NetStruct].cidr
526
776
  depth = entry[:Depth]
527
- network = IPAdmin.unpack_ip_addr(:Integer => net_struct.network,
528
- :Version => @version)
529
- netmask = IPAdmin.unpack_ip_netmask(:Integer => net_struct.netmask,
530
- :Version => @version)
531
- network = IPAdmin.shorten(network) if (@version == 6)
532
777
 
533
778
  if (depth == 0)
534
779
  indent = ""
@@ -536,7 +781,7 @@ class Tree
536
781
  indent = " " * (depth*3)
537
782
  end
538
783
 
539
- printed << "#{indent}#{network}/#{netmask}\n"
784
+ printed << "#{indent}#{cidr.desc(:Short => true)}\n"
540
785
  end
541
786
 
542
787
  return(printed)
@@ -547,45 +792,40 @@ class Tree
547
792
  #======================================#
548
793
 
549
794
 
550
-
551
-
552
- # PRIVATE METHODS
553
- private
554
-
555
795
  #==============================================================================#
556
- # add_to_branch() private
796
+ # siblings()
557
797
  #==============================================================================#
558
798
 
559
- # Add Netstruct object to an array of NetStruct's
799
+ # Return list of the sibling CIDRs of the provided CIDR address.
560
800
  #
561
801
  # - Arguments:
562
- # * NetStruct, and array of NetStruct's in which to add it
802
+ # * CIDR address or IPAdmin::CIDR object
563
803
  #
564
804
  # - Returns:
565
- # * none
805
+ # * Array of IPAdmin::CIDR objects
566
806
  #
567
- # Example:
568
- # add_to_branch(net_struct,branch)
807
+ # Examples:
808
+ # puts tree.siblings('192.168.1.0/27')
569
809
  #
570
- def add_to_branch(net_struct,branch)
571
- # make sure we have appropriate types
572
- if (!net_struct.kind_of?(IPAdmin::NetStruct) ||
573
- !branch.kind_of?(Array) )
574
- raise ArgumentError, "Incorrect argument types " +
575
- "#{net_struct.class} and #{branch}."
810
+ def siblings(cidr)
811
+ # validate object
812
+ if ( !cidr.kind_of?(IPAdmin::CIDR) )
813
+ begin
814
+ cidr = IPAdmin::CIDR.new(:CIDR => cidr)
815
+ rescue Exception => error
816
+ raise ArgumentError, "Provided argument raised the following " +
817
+ "errors: #{error}"
818
+ end
576
819
  end
577
820
 
578
- index = 0
579
- branch.each do
580
- if(net_struct.network < (branch[index]).network)
581
- break
821
+ list = []
822
+ find_parent(cidr,@root).children.each do |entry|
823
+ if (!IPAdmin.compare(cidr,entry.cidr))
824
+ list.push(entry.cidr)
582
825
  end
583
- index += 1
584
826
  end
585
827
 
586
- branch.insert(index, net_struct)
587
-
588
- return()
828
+ return(list)
589
829
  end
590
830
 
591
831
  #======================================#
@@ -594,50 +834,54 @@ private
594
834
 
595
835
 
596
836
  #==============================================================================#
597
- # contains() private
837
+ # supernets()
598
838
  #==============================================================================#
599
839
 
600
- # Is the first NetStruct object the supernet of the second?
840
+ # Return list of the top-level supernets of this tree.
601
841
  #
602
842
  # - Arguments:
603
- # * Two NetStruct objects
843
+ # * none
604
844
  #
605
845
  # - Returns:
606
- # * true if struct1 contains or is equal to struct2, or false if not.
846
+ # * Array of IPAdmin::CIDR objects
607
847
  #
608
- # Example:
609
- # is_contained = contains(struct1,struct2)
848
+ # Examples:
849
+ # supernets = tree.supernets()
610
850
  #
611
- def contains(struct1,struct2)
851
+ def supernets()
852
+ supernets = []
853
+ @root.children.each {|x| supernets.push(x.cidr)}
854
+ return (supernets)
855
+ end
612
856
 
613
- # make sure we have appropriate types
614
- if (!struct1.kind_of?(IPAdmin::NetStruct) ||
615
- !struct2.kind_of?(IPAdmin::NetStruct) )
616
- raise ArgumentError, "Incorrect argument types " +
617
- "#{struct1.class} and #{struct2.class}."
618
- end
857
+ #======================================#
858
+ #
859
+ #======================================#
619
860
 
620
- network1 = struct1.network
621
- netmask1 = struct1.netmask
622
- network2 = struct2.network
623
- netmask2 = struct2.netmask
624
- hostmask1 = netmask1 ^ @all_f
625
861
 
626
- # if network1 == network2, then we have to go by netmask length
627
- # else we can tell by or'ing network1 and network2 by hostmask1
628
- # and comparing the results
629
- is_contained = false
630
- if (network1 == network2)
631
- if (netmask2 >= netmask1)
632
- is_contained = true
633
- end
634
- else
635
- if ( (network1 | hostmask1) == (network2 | hostmask1) )
636
- is_contained = true
862
+
863
+
864
+ # PRIVATE INSTANCE METHODS
865
+ private
866
+
867
+ #==============================================================================#
868
+ # add_to_parent() private
869
+ #==============================================================================#
870
+
871
+ # Add NetStruct object to an array of NetStruct's
872
+ #
873
+ def add_to_parent(new_entry, parent)
874
+ index = 0
875
+ parent.children.each do
876
+ if(new_entry.cidr.packed_network < parent.children[index].cidr.packed_network)
877
+ break
637
878
  end
879
+ index += 1
638
880
  end
639
881
 
640
- return(is_contained)
882
+ parent.children.insert(index, new_entry)
883
+
884
+ return()
641
885
  end
642
886
 
643
887
  #======================================#
@@ -646,32 +890,21 @@ private
646
890
 
647
891
 
648
892
  #==============================================================================#
649
- # dump_branch() private
893
+ # dump_children() private
650
894
  #==============================================================================#
651
895
 
652
896
  # Dump contents of an Array of NetStruct objects
653
897
  #
654
- # - Arguments:
655
- # * array of NetStruct objects, and an optional depth within the tree
656
- #
657
- # - Returns:
658
- # * array of hashes in form: :NetStruct => IPAdmin::NetStruct object
659
- # :Depth => (depth level in tree)
660
- # Example:
661
- # dumped = dump_branch(branch)
662
- #
663
- def dump_branch(branch,depth=nil)
898
+ def dump_children(parent,depth=nil)
664
899
  list = []
665
900
  depth = 0 if (!depth)
666
901
 
667
- branch.each do |entry|
668
- subnets = entry.subnets
902
+ parent.children.each do |entry|
669
903
  list.push({:NetStruct => entry, :Depth => depth})
670
904
 
671
- if (subnets.length > 0)
672
- list.concat( dump_branch(subnets, (depth+1) ) )
905
+ if (entry.children.length > 0)
906
+ list.concat( dump_children(entry, (depth+1) ) )
673
907
  end
674
-
675
908
  end
676
909
 
677
910
  return(list)
@@ -683,46 +916,55 @@ private
683
916
 
684
917
 
685
918
  #==============================================================================#
686
- # find_home() private
919
+ # find_me() private
687
920
  #==============================================================================#
688
921
 
689
- # Find the branch to which a NetStruct object belongs.
690
- #
691
- # - Arguments:
692
- # * IPAdmin::NetStruct to find, and Array of
693
- # IPAdmin::NetStruct's to search
694
- #
695
- # - Returns:
696
- # * array of: container IPAdmin::NetStruct object,
697
- # and container branch, or nil
922
+ # Find the NetStruct to which a cidr belongs.
698
923
  #
699
- # Example:
700
- # branch = find_home(net_struct,branch)
924
+ def find_me(cidr)
925
+ me = nil
926
+
927
+ # find matching
928
+ parent = find_parent(cidr,@root)
929
+ parent.children.each do |entry|
930
+ if (entry.cidr.version == cidr.version)
931
+ if (IPAdmin.compare(entry.cidr,cidr) == 1)
932
+ me = entry
933
+ break
934
+ end
935
+ end
936
+ end
937
+
938
+ return(me)
939
+ end
940
+
941
+ #======================================#
701
942
  #
702
- def find_home(net_struct,branch)
703
- ret_val = nil
943
+ #======================================#
704
944
 
705
- branch.each do |entry|
706
- is_contained = contains(entry,net_struct)
707
945
 
708
- if (is_contained)
709
- home_struct = entry
710
- home_branch = branch
946
+ #==============================================================================#
947
+ # find_parent() private
948
+ #==============================================================================#
711
949
 
712
- if (home_struct.subnets.length > 0)
713
- search_results = find_home(net_struct,home_struct.subnets)
714
- if (search_results)
715
- home_struct = search_results[0]
716
- home_branch = search_results[1]
717
- end
718
- end
950
+ # Find the parent NetStruct to which a child NetStruct belongs.
951
+ #
952
+ def find_parent(cidr,parent)
953
+ version = cidr.version
719
954
 
720
- ret_val = [home_struct,home_branch]
955
+ parent.children.each do |entry|
956
+ if (entry.cidr.version == version && entry.cidr.contains?(cidr))
957
+ parent = entry
958
+
959
+ if (parent.children.length > 0)
960
+ search_results = find_parent(cidr,parent)
961
+ parent = search_results if (search_results)
962
+ end
721
963
  break
722
964
  end
723
965
  end
724
966
 
725
- return(ret_val)
967
+ return(parent)
726
968
  end
727
969
 
728
970
  #======================================#