junos-ez-stdlib 0.0.12 → 0.0.15

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.
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