junos-ez-stdlib 0.0.12 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGELOG.md +17 -3
  2. data/README.md +10 -0
  3. data/docs/Providers/IPports.md +61 -0
  4. data/docs/Providers/L1ports.md +29 -0
  5. data/docs/Providers/L2ports.md +43 -0
  6. data/docs/Providers/StaticHosts.md +26 -0
  7. data/docs/Providers/StaticRoutes.md +37 -0
  8. data/docs/Providers/UserAuths.md +32 -0
  9. data/docs/{Users.md → Providers/Users.md} +17 -0
  10. data/docs/Providers/Vlans.md +43 -0
  11. data/docs/Providers_Resources.md +24 -0
  12. data/docs/README_FIRST.md +27 -0
  13. data/docs/Utils/Config.md +161 -0
  14. data/docs/Utils/Filesystem.md +78 -0
  15. data/docs/Utils/Routing-Engine.md +248 -0
  16. data/examples/re_upgrade.rb +1 -1
  17. data/examples/re_utils.rb +1 -0
  18. data/examples/vlans.rb +7 -1
  19. data/lib/junos-ez/ip_ports.rb +3 -1
  20. data/lib/junos-ez/ip_ports/classic.rb +49 -14
  21. data/lib/junos-ez/l1_ports.rb +51 -11
  22. data/lib/junos-ez/l2_ports.rb +1 -1
  23. data/lib/junos-ez/l2_ports/vlan.rb +176 -60
  24. data/lib/junos-ez/provider.rb +4 -1
  25. data/lib/junos-ez/system/st_hosts.rb +0 -0
  26. data/lib/junos-ez/system/st_routes.rb +0 -0
  27. data/lib/junos-ez/system/userauths.rb +0 -0
  28. data/lib/junos-ez/system/users.rb +0 -0
  29. data/lib/junos-ez/utils/config.rb +33 -2
  30. data/lib/junos-ez/utils/re.rb +22 -3
  31. data/lib/junos-ez/vlans.rb +5 -3
  32. data/lib/junos-ez/vlans/vlan.rb +6 -0
  33. metadata +14 -13
  34. data/docs/Config_Utils.md +0 -3
  35. data/docs/Filesys_Utils.md +0 -3
  36. data/docs/IPports.md +0 -3
  37. data/docs/L1ports.md +0 -3
  38. data/docs/L2ports.md +0 -3
  39. data/docs/RE_utils.md +0 -3
  40. data/docs/StaticHosts.md +0 -3
  41. data/docs/StaticRoutes.md +0 -3
  42. data/docs/Vlans.md +0 -3
  43. data/lib/junos-ez/system/user_ssh_keys.rb +0 -113
@@ -10,6 +10,8 @@ module Junos::Ez::L1ports
10
10
  :duplex, # [ :auto, :half, :full ]
11
11
  :unit_count, # number of configured units
12
12
  ]
13
+
14
+ IFS_NAME_FILTER = '[fgx]e-*'
13
15
 
14
16
  def self.Provider( ndev, varsym )
15
17
  newbie = case ndev.fact( :ifd_style )
@@ -47,7 +49,7 @@ class Junos::Ez::L1ports::Provider < Junos::Ez::Provider::Parent
47
49
  @ndev.rpc.get_interface_information({
48
50
  :media => true,
49
51
  :terse => true,
50
- :interface_name => '[fgx]e-*'
52
+ :interface_name => Junos::Ez::L1ports::IFS_NAME_FILTER
51
53
  }).xpath('physical-interface/name').collect do |ifs|
52
54
  ifs.text.strip
53
55
  end
@@ -56,24 +58,62 @@ class Junos::Ez::L1ports::Provider < Junos::Ez::Provider::Parent
56
58
  def build_catalog
57
59
  @catalog = {}
58
60
 
59
- @ndev.rpc.get_configuration{|xml|
60
- xml.interfaces {
61
- list!.each do |ifs|
61
+ # we could have a large list of interfaces, so
62
+ # we need to break this up into individual "gets"
63
+
64
+ list!.each do |ifs_name|
65
+ @ndev.rpc.get_configuration{ |xml|
66
+ xml.interfaces {
62
67
  xml.interface {
63
- xml.name ifs
68
+ xml.name ifs_name
64
69
  xml_read_filter( xml )
65
70
  }
66
- end
67
- }
68
- }.xpath('interfaces/interface').each do |ifs_xml|
69
- ifs_name = ifs_xml.xpath('name').text
70
- @catalog[ifs_name] = {}
71
- xml_read_parser( ifs_xml, @catalog[ifs_name] )
71
+ }
72
+ }.xpath('interfaces/interface').each do |ifs_xml|
73
+ @catalog[ifs_name] = {}
74
+ xml_read_parser( ifs_xml, @catalog[ifs_name] )
75
+ end
72
76
  end
73
77
 
74
78
  return @catalog
75
79
  end
76
80
 
81
+ ### ---------------------------------------------------------------
82
+ ### Resource methods
83
+ ### ---------------------------------------------------------------
84
+
85
+ ## returns a Hash of status information, from "show interface ..."
86
+ ## basic information, not absolutely everything. but if a
87
+ ## block is given, then pass the XML to the block.
88
+
89
+ def status
90
+
91
+ got = @ndev.rpc.get_interface_information(:interface_name => @name, :media => true )
92
+ phy = got.xpath('physical-interface')[0]
93
+ return nil unless phy
94
+
95
+ ret_h = {}
96
+ ret_h[:macaddr] = phy.xpath('current-physical-address').text.strip
97
+ xml_when_item(phy.xpath('description')){|i| ret_h[:description] = i.text.strip }
98
+ ret_h[:oper_status] = phy.xpath('oper-status').text.strip
99
+ ret_h[:admin_status] = phy.xpath('admin-status').text.strip
100
+ ret_h[:mtu] = phy.xpath('mtu').text.to_i
101
+ ret_h[:speed] = {:admin => phy.xpath('speed').text.strip }
102
+ ret_h[:duplex] = {:admin => phy.xpath('duplex').text.strip }
103
+ ret_h[:autoneg] = phy.xpath('if-auto-negotiation').text.strip
104
+
105
+ if ret_h[:autoneg] == "enabled"
106
+ autoneg = phy.xpath('ethernet-autonegotiation')[0]
107
+ ret_h[:speed][:oper] = autoneg.xpath('link-partner-speed').text.strip
108
+ ret_h[:duplex][:oper] = autoneg.xpath('link-partner-duplexity').text.strip
109
+ end
110
+
111
+ # if a block is given, then it means the caller wants to process the XML data.
112
+ yield( phy ) if block_given?
113
+
114
+ ret_h
115
+ end
116
+
77
117
  end
78
118
 
79
119
  require 'junos-ez/l1_ports/switch'
@@ -6,7 +6,7 @@ module Junos::Ez::L2ports
6
6
  PROPERTIES = [
7
7
  :description, # String | nil
8
8
  :untagged_vlan, # String | nil
9
- :tagged_vlans, # Array of String | nil
9
+ :tagged_vlans, # Set of String | nil
10
10
  :vlan_tagging # true | false
11
11
  ]
12
12
 
@@ -52,27 +52,56 @@ class Junos::Ez::L2ports::Provider::VLAN < Junos::Ez::L2ports::Provider
52
52
 
53
53
  f_eth = as_xml.xpath('family/ethernet-switching')
54
54
  as_hash[:vlan_tagging] = f_eth.xpath('port-mode').text.chomp == 'trunk'
55
-
55
+
56
+ # obtain a copy of the running state, this is needed in case the config
57
+ # is located under the [edit vlans] stanza vs. [edit interfaces]
58
+
59
+ ifs_name = @name || as_xml.xpath('ancestor::interface/name').text.strip
60
+ eth_port_vlans = _get_eth_port_vlans_h( ifs_name )
61
+ @under_vlans = []
62
+
56
63
  # --- access port
64
+
57
65
  if as_hash[:vlan_tagging] == false
58
- xml_when_item(f_eth.xpath('vlan/members')){|i| as_hash[:untagged_vlan] = i.text.chomp }
66
+ xml_when_item(f_eth.xpath('vlan/members')){ |i| as_hash[:untagged_vlan] = i.text.chomp }
67
+ unless as_hash[:untagged_vlan]
68
+ as_hash[:untagged_vlan] = eth_port_vlans[:untagged]
69
+ @under_vlans << eth_port_vlans[:untagged]
70
+ end
59
71
  return
60
72
  end
61
73
 
62
74
  # --- trunk port
75
+
63
76
  xml_when_item(f_eth.xpath('native-vlan-id')){|i| as_hash[:untagged_vlan] = i.text.chomp }
64
- as_hash[:tagged_vlans] = f_eth.xpath('vlan/members').collect { |v| v.text.chomp }
77
+ as_hash[:untagged_vlan] ||= eth_port_vlans[:untagged]
78
+ as_hash[:tagged_vlans] = f_eth.xpath('vlan/members').collect { |v| v.text.chomp }.to_set
79
+ (eth_port_vlans[:tagged] - as_hash[:tagged_vlans]).each do |vlan|
80
+ as_hash[:tagged_vlans] << vlan
81
+ @under_vlans << vlan
82
+ end
83
+
65
84
  end
66
85
 
86
+ ### ---------------------------------------------------------------
87
+ ### XML on_create, on_delete handlers
88
+ ### ---------------------------------------------------------------
89
+
90
+ ## overload the xml_on_delete method since we may need
91
+ ## to do some cleanup work in the [edit vlans] stanza
92
+
93
+ def xml_on_delete( xml )
94
+ return unless @under_vlans
95
+ return if @under_vlans.empty?
96
+
97
+ _xml_rm_under_vlans( xml, @under_vlans )
98
+ end
99
+
67
100
  ### ---------------------------------------------------------------
68
101
  ### XML property writers
69
102
  ### ---------------------------------------------------------------
70
103
 
71
- ## overload the xml_build_change method so we can 'copy-thru'
72
- ## some of the has -> should values. this way we don't need
73
- ## to munge all of the state-transition code.
74
-
75
- def xml_build_at_here( xml )
104
+ def xml_at_here( xml )
76
105
  xml.family {
77
106
  xml.send(:'ethernet-switching') {
78
107
  return xml
@@ -80,11 +109,16 @@ class Junos::Ez::L2ports::Provider::VLAN < Junos::Ez::L2ports::Provider
80
109
  }
81
110
  end
82
111
 
83
- def xml_build_change( xml_at_here = nil )
84
- @should[:untagged_vlan] ||= @has[:untagged_vlan]
85
- super xml_build_at_here( xml_at_top )
86
- end
87
-
112
+ def xml_build_change( nop = nil )
113
+ @under_vlans ||= [] # handles case for create'd port
114
+
115
+ if mode_changed?
116
+ @should[:untagged_vlan] ||= @has[:untagged_vlan]
117
+ end
118
+
119
+ super xml_at_here( xml_at_top )
120
+ end
121
+
88
122
  ## ----------------------------------------------------------------
89
123
  ## :description
90
124
  ## ----------------------------------------------------------------
@@ -110,6 +144,7 @@ class Junos::Ez::L2ports::Provider::VLAN < Junos::Ez::L2ports::Provider
110
144
  # when the vlan_tagging value changes then this method
111
145
  # will trigger updates to the untagged_vlan and tagged_vlans
112
146
  # resource values as well.
147
+ # !!! DO NOT SWAP THIS ORDER untagged processing *MUST* BE FIRST!
113
148
 
114
149
  upd_untagged_vlan( xml )
115
150
  upd_tagged_vlans( xml )
@@ -129,20 +164,23 @@ class Junos::Ez::L2ports::Provider::VLAN < Junos::Ez::L2ports::Provider
129
164
  def upd_tagged_vlans( xml )
130
165
  return false unless should_trunk?
131
166
 
132
- v_should = @should[:tagged_vlans] || []
133
-
134
- if v_should.empty?
135
- xml.vlan Netconf::JunosConfig::DELETE
136
- return true
137
- end
138
-
139
- v_has = @has[:tagged_vlans] || []
140
- v_has = v_has.map(&:to_s)
141
- v_should = v_should.map(&:to_s)
167
+ @should[:tagged_vlans] = @should[:tagged_vlans].to_set if @should[:tagged_vlans].kind_of? Array
168
+ @has[:tagged_vlans] = @has[:tagged_vlans].to_set if @has[:tagged_vlans].kind_of? Array
169
+
170
+ v_should = @should[:tagged_vlans] || Set.new
171
+ v_has = @has[:tagged_vlans] || Set.new
142
172
 
143
173
  del = v_has - v_should
144
174
  add = v_should - v_has
145
-
175
+
176
+ del_under_vlans = del & @under_vlans
177
+
178
+ unless del_under_vlans.empty?
179
+ del = del ^ @under_vlans
180
+ _xml_rm_under_vlans( xml, del_under_vlans )
181
+ @under_vlans = []
182
+ end
183
+
146
184
  if add or del
147
185
  xml.vlan {
148
186
  del.each { |v| xml.members v, Netconf::JunosConfig::DELETE }
@@ -226,46 +264,46 @@ class Junos::Ez::L2ports::Provider::VLAN
226
264
  ### -------------------------------------------------------------
227
265
 
228
266
  def self.ac_ac_nountg( this, xml )
229
- xml.vlan Netconf::JunosConfig::DELETE
267
+ this._xml_rm_ac_untagged_vlan( xml )
230
268
  end
231
269
 
232
270
  def self.ac_tr_nountg( this, xml )
233
- unless (untg_vlan = this.has[:tagged_vlans]).nil?
234
- xml.vlan {
235
- xml.members untg_vlan, Netconf::JunosConfig::DELETE
236
- }
271
+ unless (untg_vlan = this.has[:untagged_vlan]).nil?
272
+ this._xml_rm_ac_untagged_vlan( xml )
237
273
  end
238
274
  end
239
275
 
240
276
  def self.tr_ac_nountg( this, xml )
241
- xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
242
- xml.vlan( Netconf::JunosConfig::DELETE ) if this.has[:tagged_vlans]
277
+ xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
278
+ this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]
243
279
  end
244
280
 
245
281
  def self.tr_tr_nountg( this, xml )
246
282
  xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
247
283
  end
248
284
 
285
+ ## ----------------------------------------------------------------
286
+ ## transition where port WILL-HAVE untagged-vlan
287
+ ## ----------------------------------------------------------------
288
+
249
289
  def self.ac_ac_untg( this, xml )
250
- xml.vlan( Netconf::JunosConfig::REPLACE ) {
290
+ this._xml_rm_ac_untagged_vlan( xml )
291
+ xml.vlan {
251
292
  xml.members this.should[:untagged_vlan]
252
293
  }
253
294
  end
254
295
 
255
296
  def self.ac_tr_untg( this, xml )
297
+ # move untagged vlan to native-vlan-id ...
256
298
  was_untg_vlan = this.has[:untagged_vlan]
257
-
258
- xml.vlan( Netconf::JunosConfig::REPLACE ) {
259
- xml.members was_untg_vlan, Netconf::JunosConfig::DELETE if was_untg_vlan
260
- }
261
- xml.send :'native-vlan-id', this.should[:untagged_vlan]
299
+ xml.send :'native-vlan-id', this.should[:untagged_vlan]
300
+ this._xml_rm_ac_untagged_vlan( xml ) if was_untg_vlan
262
301
  end
263
302
 
264
- def self.tr_ac_untg( this, xml )
265
- xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
266
- xml.vlan( Netconf::JunosConfig::REPLACE ) {
267
- xml.members this.should[:untagged_vlan]
268
- }
303
+ def self.tr_ac_untg( this, xml )
304
+ xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
305
+ this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]
306
+ xml.vlan { xml.members this.should[:untagged_vlan] }
269
307
  end
270
308
 
271
309
  def self.tr_tr_untg( this, xml )
@@ -293,25 +331,103 @@ class Junos::Ez::L2ports::Provider::VLAN
293
331
 
294
332
  def build_catalog
295
333
  @catalog = {}
296
- return @catalog if list.empty?
334
+ return @catalog if list!.empty?
335
+
336
+ list.each do |ifs_name|
337
+ @ndev.rpc.get_configuration{ |xml|
338
+ xml.interfaces {
339
+ xml.interface {
340
+ xml.name ifs_name
341
+ xml.unit { xml.name '0' }
342
+ }
343
+ }
344
+ }.xpath('interfaces/interface').each do |ifs_xml|
345
+ @catalog[ifs_name] = {}
346
+ unit = ifs_xml.xpath('unit')[0]
347
+ xml_read_parser( unit, @catalog[ifs_name] )
348
+ end
349
+ end
297
350
 
298
- @ndev.rpc.get_configuration{ |xml|
299
- xml.interfaces {
300
- list.each do |port_name|
301
- Nokogiri::XML::Builder.with( xml.parent ) do |x1|
302
- x1.interface { x1.name port_name
303
- x1.unit { x1.name '0' }
304
- }
305
- end
306
- end
307
- }
308
- }.xpath('interfaces/interface').each do |ifs|
309
- ifs_name = ifs.xpath('name').text
310
- unit = ifs.xpath('unit')[0]
311
- @catalog[ifs_name] = {}
312
- xml_read_parser( unit, @catalog[ifs_name] )
313
- end
314
351
  @catalog
315
352
  end
316
353
 
317
- end
354
+ end
355
+
356
+ ##### ---------------------------------------------------------------
357
+ ##### !!!!! PRIVATE METHODS !!!!
358
+ ##### ---------------------------------------------------------------
359
+
360
+ class Junos::Ez::L2ports::Provider::VLAN
361
+ private
362
+
363
+ def _get_eth_port_vlans_h( ifs_name )
364
+
365
+ got = @ndev.rpc.get_ethernet_switching_interface_information(:interface_name => ifs_name)
366
+ ret_h = {:untagged => nil, :tagged => Set.new }
367
+ got.xpath('//interface-vlan-member').each do |vlan|
368
+ vlan_name = vlan.xpath('interface-vlan-name').text.strip
369
+ tgdy = vlan.xpath('interface-vlan-member-tagness').text.strip
370
+ if tgdy == 'untagged'
371
+ ret_h[:untagged] = vlan_name
372
+ else
373
+ ret_h[:tagged] << vlan_name
374
+ end
375
+ end
376
+ ret_h
377
+ end
378
+
379
+ end
380
+
381
+
382
+ ### ---------------------------------------------------------------
383
+ ### [edit vlans] - for interfaces configured here ...
384
+ ### ---------------------------------------------------------------
385
+
386
+ class Junos::Ez::L2ports::Provider::VLAN
387
+
388
+ def _xml_edit_under_vlans( xml )
389
+ Nokogiri::XML::Builder.with( xml.doc.root ) do |dot|
390
+ dot.vlans {
391
+ return dot
392
+ }
393
+ end
394
+ end
395
+
396
+ def _xml_rm_under_vlans( xml, vlans )
397
+ at_vlans = _xml_edit_under_vlans( xml )
398
+ vlans.each do |vlan_name|
399
+ Nokogiri::XML::Builder.with( at_vlans.parent ) do |this|
400
+ this.vlan {
401
+ this.name vlan_name
402
+ this.interface( Netconf::JunosConfig::DELETE ) { this.name @name }
403
+ }
404
+ end
405
+ end
406
+ end
407
+
408
+ def _xml_rm_ac_untagged_vlan( xml )
409
+ if @under_vlans.empty?
410
+ xml.vlan Netconf::JunosConfig::DELETE
411
+ else
412
+ _xml_rm_under_vlans( xml, [ @has[:untagged_vlan ] ] )
413
+ @under_vlans = []
414
+ end
415
+ end
416
+
417
+ def _xml_rm_these_vlans( xml, vlans )
418
+ if @under_vlans.empty?
419
+ xml.vlan( Netconf::JunosConfig::DELETE )
420
+ else
421
+ # could be a mix between [edit vlans] and [edit interfaces] ...
422
+ v_has = vlans.to_set
423
+ del_under_vlans = v_has & @under_vlans
424
+ _xml_rm_under_vlans( xml, del_under_vlans )
425
+ if v_has ^ @under_vlans
426
+ xml.vlan( Netconf::JunosConfig::DELETE )
427
+ end
428
+ @under_vlans = []
429
+ end
430
+ end
431
+
432
+ end
433
+
@@ -5,11 +5,13 @@
5
5
  ##### scope of impact. Thank you.
6
6
  ##### ---------------------------------------------------------------
7
7
 
8
+ require 'set'
9
+
8
10
  module Junos; end
9
11
 
10
12
  module Junos::Ez
11
13
 
12
- VERSION = "0.0.12"
14
+ VERSION = "0.0.15"
13
15
 
14
16
  ### ---------------------------------------------------------------
15
17
  ### rpc_errors - decodes the XML into an array of error/Hash
@@ -286,6 +288,7 @@ class Junos::Ez::Provider::Parent
286
288
  xml = xml_at_top
287
289
  par = xml.instance_variable_get(:@parent)
288
290
  par['delete'] = 'delete'
291
+ xml_on_delete( xml )
289
292
  rsp = write_xml_config!( xml.doc.root )
290
293
  @has[:_exist] = false
291
294
  true # rsp ... don't return XML, but let's hear from the community...
File without changes
File without changes
File without changes
File without changes
@@ -10,6 +10,7 @@ configuration files/templates and software images
10
10
  lock! - take exclusive lock on config
11
11
  unlock! - release exclusive lock on config
12
12
  rollback! - perform a config rollback
13
+ get_config - returns requested config in "text" format-style
13
14
 
14
15
  ---------------------------------------------------------------------
15
16
  =end
@@ -162,7 +163,7 @@ class Junos::Ez::Config::Provider < Junos::Ez::Provider::Parent
162
163
  ### current candidate configuration loaded and the rollback_id
163
164
  ###
164
165
  ### --- returns ---
165
- ### false if no diff
166
+ ### nil if no diff
166
167
  ### String of diff output otherwise
167
168
  ### ---------------------------------------------------------------
168
169
 
@@ -170,7 +171,7 @@ class Junos::Ez::Config::Provider < Junos::Ez::Provider::Parent
170
171
  raise ArgumentError, "invalid rollback #{rollback_id}" unless ( rollback_id >= 0 and rollback_id <= 50 )
171
172
  got = ndev.rpc.get_configuration( :compare=>'rollback', :rollback=> rollback_id.to_s )
172
173
  diff = got.xpath('configuration-output').text
173
- return false if diff == "\n"
174
+ return nil if diff == "\n"
174
175
  diff
175
176
  end
176
177
 
@@ -200,6 +201,36 @@ class Junos::Ez::Config::Provider < Junos::Ez::Provider::Parent
200
201
  true
201
202
  end
202
203
 
204
+ ### ---------------------------------------------------------------
205
+ ### get_config - returns String of requested (or entire) config
206
+ ### in "text" (curly-brace) format. The 'rqst' argument
207
+ ### identifies the scope of the config, for example:
208
+ ###
209
+ ### .get_config( "interfaces ge-0/0/0" )
210
+ ###
211
+ ### If there is no configuration available, 'nil' is returned
212
+ ###
213
+ ### If there is an error in the request, that will be returned
214
+ ### as a String with "ERROR!" prepended
215
+ ### ---------------------------------------------------------------
216
+
217
+ def get_config( rqst = nil )
218
+ scope = "show configuration"
219
+ scope.concat( " " + rqst ) if rqst
220
+ begin
221
+ @ndev.rpc.command( scope, :format => 'text' ).xpath('configuration-output').text
222
+ rescue NoMethodError
223
+ # indicates no configuration found
224
+ nil
225
+ rescue => e
226
+ # indicates error in request
227
+ err = e.rsp.xpath('rpc-error')[0]
228
+ err_info = err.xpath('error-info/bad-element').text
229
+ err_msg = err.xpath('error-message').text
230
+ "ERROR! " + err_msg + ": " + err_info
231
+ end
232
+ end
233
+
203
234
  end # class Provider
204
235
 
205
236