ipadmin 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
  #======================================#