shopify-junos-ez-stdlib 1.0.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.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +91 -0
  3. data/LICENSE +26 -0
  4. data/README.md +199 -0
  5. data/docs/Facts.md +192 -0
  6. data/docs/Providers/Group.md +61 -0
  7. data/docs/Providers/IPports.md +61 -0
  8. data/docs/Providers/L1ports.md +29 -0
  9. data/docs/Providers/L2ports.md +43 -0
  10. data/docs/Providers/LAGports.md +57 -0
  11. data/docs/Providers/StaticHosts.md +26 -0
  12. data/docs/Providers/StaticRoutes.md +37 -0
  13. data/docs/Providers/UserAuths.md +32 -0
  14. data/docs/Providers/Users.md +122 -0
  15. data/docs/Providers/Vlans.md +43 -0
  16. data/docs/Providers_Resources.md +353 -0
  17. data/docs/README_FIRST.md +27 -0
  18. data/docs/Utils/Config.md +160 -0
  19. data/docs/Utils/Filesystem.md +360 -0
  20. data/docs/Utils/Routing-Engine.md +379 -0
  21. data/docs/Utils/SCP.md +24 -0
  22. data/examples/config/config_file.rb +72 -0
  23. data/examples/config/config_template_object.rb +81 -0
  24. data/examples/config/config_template_simple.rb +76 -0
  25. data/examples/config/multi_config.rb +60 -0
  26. data/examples/fs_utils.rb +31 -0
  27. data/examples/lag_port.rb +27 -0
  28. data/examples/re_upgrade.rb +99 -0
  29. data/examples/re_utils.rb +33 -0
  30. data/examples/simple.rb +46 -0
  31. data/examples/st_hosts.rb +33 -0
  32. data/examples/user.rb +32 -0
  33. data/examples/vlans.rb +31 -0
  34. data/junos-ez-stdlib.gemspec +15 -0
  35. data/lib/junos-ez/exceptions.rb +3 -0
  36. data/lib/junos-ez/facts.rb +83 -0
  37. data/lib/junos-ez/facts/chassis.rb +51 -0
  38. data/lib/junos-ez/facts/ifd_style.rb +17 -0
  39. data/lib/junos-ez/facts/personality.rb +25 -0
  40. data/lib/junos-ez/facts/switch_style.rb +31 -0
  41. data/lib/junos-ez/facts/version.rb +58 -0
  42. data/lib/junos-ez/group.rb +206 -0
  43. data/lib/junos-ez/ip_ports.rb +30 -0
  44. data/lib/junos-ez/ip_ports/classic.rb +188 -0
  45. data/lib/junos-ez/l1_ports.rb +121 -0
  46. data/lib/junos-ez/l1_ports/classic.rb +87 -0
  47. data/lib/junos-ez/l1_ports/switch.rb +134 -0
  48. data/lib/junos-ez/l2_ports.rb +66 -0
  49. data/lib/junos-ez/l2_ports/bridge_domain.rb +499 -0
  50. data/lib/junos-ez/l2_ports/vlan.rb +433 -0
  51. data/lib/junos-ez/l2_ports/vlan_l2ng.rb +502 -0
  52. data/lib/junos-ez/lag_ports.rb +268 -0
  53. data/lib/junos-ez/provider.rb +619 -0
  54. data/lib/junos-ez/stdlib.rb +18 -0
  55. data/lib/junos-ez/system.rb +48 -0
  56. data/lib/junos-ez/system/st_hosts.rb +92 -0
  57. data/lib/junos-ez/system/st_routes.rb +159 -0
  58. data/lib/junos-ez/system/syscfg.rb +103 -0
  59. data/lib/junos-ez/system/userauths.rb +84 -0
  60. data/lib/junos-ez/system/users.rb +217 -0
  61. data/lib/junos-ez/utils/config.rb +236 -0
  62. data/lib/junos-ez/utils/fs.rb +385 -0
  63. data/lib/junos-ez/utils/re.rb +558 -0
  64. data/lib/junos-ez/version.rb +6 -0
  65. data/lib/junos-ez/vlans.rb +38 -0
  66. data/lib/junos-ez/vlans/bridge_domain.rb +89 -0
  67. data/lib/junos-ez/vlans/vlan.rb +119 -0
  68. data/lib/junos-ez/vlans/vlan_l2ng.rb +126 -0
  69. data/shipit.yml +4 -0
  70. data/tmp +7 -0
  71. metadata +129 -0
@@ -0,0 +1,433 @@
1
+ class Junos::Ez::L2ports::Provider::VLAN < Junos::Ez::L2ports::Provider
2
+
3
+ ### ---------------------------------------------------------------
4
+ ### XML top placement
5
+ ### ---------------------------------------------------------------
6
+
7
+ def xml_at_top
8
+ Nokogiri::XML::Builder.new {|xml| xml.configuration {
9
+ xml.interfaces {
10
+ return xml_at_element_top( xml, @name )
11
+ }
12
+ }}
13
+ end
14
+
15
+ # set the edit anchor inside the ethernet-switching stanza
16
+ # we will need to 'up-out' when making changes to the
17
+ # unit information, like description
18
+
19
+ def xml_at_element_top( xml, name )
20
+ xml.interface {
21
+ xml.name name
22
+ xml.unit {
23
+ xml.name '0'
24
+ return xml
25
+ }
26
+ }
27
+ end
28
+
29
+ ### ---------------------------------------------------------------
30
+ ### XML property readers
31
+ ### ---------------------------------------------------------------
32
+
33
+ def xml_get_has_xml( xml )
34
+ # second unit contains the family/ethernet-switching stanza
35
+ got = xml.xpath('//unit')[0]
36
+
37
+ # if this resource doesn't exist we need to default some
38
+ # values into has/should variables
39
+
40
+ unless got
41
+ @has[:vlan_tagging] = false
42
+ @should = @has.clone
43
+ end
44
+
45
+ got
46
+ end
47
+
48
+ def xml_read_parser( as_xml, as_hash )
49
+ set_has_status( as_xml, as_hash )
50
+
51
+ xml_when_item(as_xml.xpath('description')){|i| as_hash[:description] = i.text}
52
+
53
+ f_eth = as_xml.xpath('family/ethernet-switching')
54
+ as_hash[:vlan_tagging] = f_eth.xpath('interface-mode').text.chomp == 'trunk'
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
+
63
+ # --- access port
64
+
65
+ if as_hash[:vlan_tagging] == false
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
71
+ return
72
+ end
73
+
74
+ # --- trunk port
75
+
76
+ xml_when_item(f_eth.xpath('native-vlan-id')){|i| as_hash[:untagged_vlan] = i.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
+
84
+ end
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
+
100
+ ### ---------------------------------------------------------------
101
+ ### XML property writers
102
+ ### ---------------------------------------------------------------
103
+
104
+ def xml_at_here( xml )
105
+ xml.family {
106
+ xml.send(:'ethernet-switching') {
107
+ return xml
108
+ }
109
+ }
110
+ end
111
+
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
+
122
+ ## ----------------------------------------------------------------
123
+ ## :description
124
+ ## ----------------------------------------------------------------
125
+
126
+ ## overload default method since we need to "up-out" of the
127
+ ## ethernet-switching stanza
128
+
129
+ def xml_change_description( xml )
130
+ unit = xml.parent.xpath('ancestor::unit')[0]
131
+ Nokogiri::XML::Builder.with( unit ){ |x|
132
+ xml_set_or_delete( x, 'description', @should[:description] )
133
+ }
134
+ end
135
+
136
+ ## ----------------------------------------------------------------
137
+ ## :vlan_tagging
138
+ ## ----------------------------------------------------------------
139
+
140
+ def xml_change_vlan_tagging( xml )
141
+ interface_mode = should_trunk? ? 'trunk' : 'access'
142
+ xml.send(:'interface-mode', interface_mode )
143
+
144
+ # when the vlan_tagging value changes then this method
145
+ # will trigger updates to the untagged_vlan and tagged_vlans
146
+ # resource values as well.
147
+ # !!! DO NOT SWAP THIS ORDER untagged processing *MUST* BE FIRST!
148
+
149
+ upd_untagged_vlan( xml )
150
+ upd_tagged_vlans( xml )
151
+
152
+ return true
153
+ end
154
+
155
+ ## ----------------------------------------------------------------
156
+ ## :tagged_vlans
157
+ ## ----------------------------------------------------------------
158
+
159
+ def xml_change_tagged_vlans( xml )
160
+ return false if mode_changed?
161
+ upd_tagged_vlans( xml )
162
+ end
163
+
164
+ def upd_tagged_vlans( xml )
165
+ return false unless should_trunk?
166
+
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
172
+
173
+ del = v_has - v_should
174
+ add = v_should - v_has
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
+
184
+ if add or del
185
+ xml.vlan {
186
+ del.each { |v| xml.members v, Netconf::JunosConfig::DELETE }
187
+ add.each { |v| xml.members v }
188
+ }
189
+ end
190
+
191
+ return true
192
+ end
193
+
194
+ ## ----------------------------------------------------------------
195
+ ## :untagged_vlan
196
+ ## ----------------------------------------------------------------
197
+
198
+ def xml_change_untagged_vlan( xml )
199
+ return false if mode_changed?
200
+ upd_untagged_vlan( xml )
201
+ end
202
+
203
+ def upd_untagged_vlan( xml )
204
+ self.class.change_untagged_vlan( self, xml )
205
+ end
206
+
207
+ end
208
+
209
+ ##### ---------------------------------------------------------------
210
+ ##### Class methods for handling state-transitions between
211
+ ##### configurations (tagged/untagged)
212
+ ##### ---------------------------------------------------------------
213
+
214
+ class Junos::Ez::L2ports::Provider::VLAN
215
+
216
+ # creating some class definitions ...
217
+ # this is a bit complicated because we need to handle port-mode
218
+ # change transitions; basically dealing with the fact that
219
+ # trunk ports use 'native-vlan-id' and access ports have a
220
+ # vlan member definition; i.e. they don't use native-vlan-id, ugh.
221
+ # Rather than doing all this logic as if/then/else statements,
222
+ # I've opted to using a proc jump-table technique. Lessons
223
+ # learned from lots of embedded systems programming :-)
224
+
225
+ def self.init_jump_table
226
+
227
+ # auto-hash table, majik!
228
+ hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
229
+
230
+ # ------------------------------------------------------------------
231
+ # - jump table for handling various untagged vlan change use-cases
232
+ # ------------------------------------------------------------------
233
+ # There are three criteria for selection:
234
+ # | is_trunk | will_trunk | no_untg |
235
+ # ------------------------------------------------------------------
236
+
237
+ # - will not have untagged vlan
238
+ hash[false][false][true] = self.method(:ac_ac_nountg)
239
+ hash[false][true][true] = self.method(:ac_tr_nountg)
240
+ hash[true][false][true] = self.method(:tr_ac_nountg)
241
+ hash[true][true][true] = self.method(:tr_tr_nountg)
242
+
243
+ # - will have untagged vlan
244
+ hash[false][false][false] = self.method(:ac_ac_untg)
245
+ hash[false][true][false] = self.method(:ac_tr_untg)
246
+ hash[true][false][false] = self.method(:tr_ac_untg)
247
+ hash[true][true][false] = self.method(:tr_tr_untg)
248
+
249
+ hash
250
+ end
251
+
252
+ ### invoke the correct method from the jump table
253
+ ### based on the three criteria to select the action
254
+
255
+ def self.change_untagged_vlan( this, xml )
256
+ @@ez_l2_jmptbl ||= init_jump_table
257
+ proc = @@ez_l2_jmptbl[this.is_trunk?][this.should_trunk?][this.should[:untagged_vlan].nil?]
258
+ proc.call( this, xml )
259
+ end
260
+
261
+ ### -------------------------------------------------------------
262
+ ### The following are all the change transition functions for
263
+ ### each of the use-cases
264
+ ### -------------------------------------------------------------
265
+
266
+ def self.ac_ac_nountg( this, xml )
267
+ this._xml_rm_ac_untagged_vlan( xml )
268
+ end
269
+
270
+ def self.ac_tr_nountg( this, xml )
271
+ unless (untg_vlan = this.has[:untagged_vlan]).nil?
272
+ this._xml_rm_ac_untagged_vlan( xml )
273
+ end
274
+ end
275
+
276
+ def self.tr_ac_nountg( this, xml )
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]
279
+ end
280
+
281
+ def self.tr_tr_nountg( this, xml )
282
+ xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
283
+ end
284
+
285
+ ## ----------------------------------------------------------------
286
+ ## transition where port WILL-HAVE untagged-vlan
287
+ ## ----------------------------------------------------------------
288
+
289
+ def self.ac_ac_untg( this, xml )
290
+ this._xml_rm_ac_untagged_vlan( xml )
291
+ xml.vlan {
292
+ xml.members this.should[:untagged_vlan]
293
+ }
294
+ end
295
+
296
+ def self.ac_tr_untg( this, xml )
297
+ # move untagged vlan to native-vlan-id ...
298
+ was_untg_vlan = this.has[:untagged_vlan]
299
+ xml.send :'native-vlan-id', this.should[:untagged_vlan]
300
+ this._xml_rm_ac_untagged_vlan( xml ) if was_untg_vlan
301
+ end
302
+
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] }
307
+ end
308
+
309
+ def self.tr_tr_untg( this, xml )
310
+ xml.send :'native-vlan-id', this.should[:untagged_vlan]
311
+ end
312
+ end
313
+
314
+ ##### ---------------------------------------------------------------
315
+ ##### Provider collection methods
316
+ ##### ---------------------------------------------------------------
317
+
318
+ class Junos::Ez::L2ports::Provider::VLAN
319
+
320
+ def build_list
321
+
322
+ begin
323
+ got = @ndev.rpc.get_ethernet_switching_interface_information(:summary=>true)
324
+ rescue => e
325
+ # in this case, no ethernet-switching is enabled so return empty list
326
+ return []
327
+ end
328
+
329
+ got.xpath('interface/interface-name').collect{ |ifn| ifn.text.split('.')[0] }
330
+ end
331
+
332
+ def build_catalog
333
+ @catalog = {}
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
350
+
351
+ @catalog
352
+ end
353
+
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
+
@@ -0,0 +1,502 @@
1
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG < Junos::Ez::L2ports::Provider
2
+
3
+ ### ---------------------------------------------------------------
4
+ ### XML top placement
5
+ ### ---------------------------------------------------------------
6
+
7
+ def xml_at_top
8
+ Nokogiri::XML::Builder.new {|xml| xml.configuration {
9
+ xml.interfaces {
10
+ return xml_at_element_top( xml, @name )
11
+ }
12
+ }}
13
+ end
14
+
15
+ # set the edit anchor inside the ethernet-switching stanza
16
+ # we will need to 'up-out' when making changes to the
17
+ # unit information, like description
18
+
19
+ def xml_at_element_top( xml, name )
20
+ xml.interface {
21
+ xml.name name
22
+ xml.send(:'native-vlan-id')
23
+ xml.unit {
24
+ xml.name '0'
25
+ return xml
26
+ }
27
+ }
28
+ end
29
+
30
+ ### ---------------------------------------------------------------
31
+ ### XML property readers
32
+ ### ---------------------------------------------------------------
33
+
34
+ def xml_get_has_xml( xml )
35
+ # second unit contains the family/ethernet-switching stanza
36
+ got = xml.xpath('//unit')[0]
37
+
38
+ # if this resource doesn't exist we need to default some
39
+ # values into has/should variables
40
+
41
+ unless got
42
+ @has[:vlan_tagging] = false
43
+ @should = @has.clone
44
+ end
45
+
46
+ got
47
+ end
48
+
49
+ def xml_read_parser( as_xml, as_hash )
50
+ ## reading is anchored at the [... unit 0 ...] level
51
+ set_has_status( as_xml, as_hash )
52
+
53
+ xml_when_item(as_xml.xpath('description')){|i| as_hash[:description] = i.text}
54
+
55
+ f_eth = as_xml.xpath('family/ethernet-switching')
56
+ as_hash[:vlan_tagging] = f_eth.xpath('interface-mode').text.chomp == 'trunk'
57
+
58
+ # obtain a copy of the running state, this is needed in case the config
59
+ # is located under the [edit vlans] stanza vs. [edit interfaces]
60
+
61
+ ifs_name = @name || as_xml.xpath('ancestor::interface/name').text.strip
62
+ eth_port_vlans = _get_eth_port_vlans_h( ifs_name )
63
+ @under_vlans = []
64
+
65
+ # --- access port
66
+
67
+ if as_hash[:vlan_tagging] == false
68
+ xml_when_item(f_eth.xpath('vlan/members')){ |i| as_hash[:untagged_vlan] = i.text.chomp }
69
+ unless as_hash[:untagged_vlan]
70
+ as_hash[:untagged_vlan] = eth_port_vlans[:untagged]
71
+ @under_vlans << eth_port_vlans[:untagged]
72
+ end
73
+ return
74
+ end
75
+
76
+ # --- trunk port
77
+
78
+ as_hash[:untagged_vlan] ||= eth_port_vlans[:untagged]
79
+ as_hash[:tagged_vlans] = f_eth.xpath('vlan/members').collect { |v| v.text.chomp }.to_set
80
+ (eth_port_vlans[:tagged] - as_hash[:tagged_vlans]).each do |vlan|
81
+ as_hash[:tagged_vlans] << vlan
82
+ @under_vlans << vlan
83
+ end
84
+
85
+ # native-vlan-id is set at the interface level, and is the VLAN-ID, not the vlan
86
+ # name. So we need to do a bit of translating here. The *ASSUMPTION* is that the
87
+ # native-vlan-id value is a given VLAN in the tagged_vlan list. So we will use
88
+ # that list to do the reverse lookup on the tag-id => name
89
+
90
+ xml_when_item(f_eth.xpath('ancestor::interface/native-vlan-id')){ |i|
91
+ as_hash[:untagged_vlan] = _vlan_tag_id_to_name( i.text.chomp, as_hash )
92
+ }
93
+ end
94
+
95
+ ### ---------------------------------------------------------------
96
+ ### XML on_create, on_delete handlers
97
+ ### ---------------------------------------------------------------
98
+
99
+ ## overload the xml_on_delete method since we may need
100
+ ## to do some cleanup work in the [edit vlans] stanza
101
+
102
+ def xml_on_delete( xml )
103
+ @ifd = xml.instance_variable_get(:@parent).at_xpath('ancestor::interface')
104
+ @ifd.xpath('//native-vlan-id').remove ## remove the element from the get-config
105
+
106
+ return unless @under_vlans
107
+ return if @under_vlans.empty?
108
+
109
+ _xml_rm_under_vlans( xml, @under_vlans )
110
+ end
111
+
112
+ ### ---------------------------------------------------------------
113
+ ### XML property writers
114
+ ### ---------------------------------------------------------------
115
+
116
+ def xml_at_here( xml )
117
+ @ifd = xml.instance_variable_get(:@parent).at_xpath('ancestor::interface')
118
+ @ifd.xpath('//native-vlan-id').remove ## remove the element from the get-config
119
+
120
+ xml.family {
121
+ xml.send(:'ethernet-switching') {
122
+ return xml
123
+ }
124
+ }
125
+ end
126
+
127
+ def xml_build_change( nop = nil )
128
+ @under_vlans ||= [] # handles case for create'd port
129
+
130
+ if mode_changed?
131
+ @should[:untagged_vlan] ||= @has[:untagged_vlan]
132
+ end
133
+
134
+ super xml_at_here( xml_at_top )
135
+ end
136
+
137
+ ## ----------------------------------------------------------------
138
+ ## :description
139
+ ## ----------------------------------------------------------------
140
+
141
+ ## overload default method since we need to "up-out" of the
142
+ ## ethernet-switching stanza
143
+
144
+ def xml_change_description( xml )
145
+ unit = xml.parent.xpath('ancestor::unit')[0]
146
+ Nokogiri::XML::Builder.with( unit ){ |x|
147
+ xml_set_or_delete( x, 'description', @should[:description] )
148
+ }
149
+ end
150
+
151
+ ## ----------------------------------------------------------------
152
+ ## :vlan_tagging
153
+ ## ----------------------------------------------------------------
154
+
155
+ def xml_change_vlan_tagging( xml )
156
+ port_mode = should_trunk? ? 'trunk' : 'access'
157
+ xml.send(:'interface-mode', port_mode )
158
+
159
+ # when the vlan_tagging value changes then this method
160
+ # will trigger updates to the untagged_vlan and tagged_vlans
161
+ # resource values as well.
162
+ # !!! DO NOT SWAP THIS ORDER untagged processing *MUST* BE FIRST!
163
+
164
+ upd_untagged_vlan( xml )
165
+ upd_tagged_vlans( xml )
166
+
167
+ return true
168
+ end
169
+
170
+ ## ----------------------------------------------------------------
171
+ ## :tagged_vlans
172
+ ## ----------------------------------------------------------------
173
+
174
+ def xml_change_tagged_vlans( xml )
175
+ return false if mode_changed?
176
+ upd_tagged_vlans( xml )
177
+ end
178
+
179
+ def upd_tagged_vlans( xml )
180
+ return false unless should_trunk?
181
+
182
+ @should[:tagged_vlans] = @should[:tagged_vlans].to_set if @should[:tagged_vlans].kind_of? Array
183
+ @has[:tagged_vlans] = @has[:tagged_vlans].to_set if @has[:tagged_vlans].kind_of? Array
184
+
185
+ v_should = @should[:tagged_vlans] || Set.new
186
+ v_has = @has[:tagged_vlans] || Set.new
187
+
188
+ del = v_has - v_should
189
+ add = v_should - v_has
190
+
191
+ del_under_vlans = del & @under_vlans
192
+
193
+ unless del_under_vlans.empty?
194
+ del = del ^ @under_vlans
195
+ _xml_rm_under_vlans( xml, del_under_vlans )
196
+ @under_vlans = []
197
+ end
198
+
199
+ if add or del
200
+ xml.vlan {
201
+ del.each { |v| xml.members v, Netconf::JunosConfig::DELETE }
202
+ add.each { |v| xml.members v }
203
+ }
204
+ end
205
+
206
+ return true
207
+ end
208
+
209
+ ## ----------------------------------------------------------------
210
+ ## :untagged_vlan
211
+ ## ----------------------------------------------------------------
212
+
213
+ def xml_change_untagged_vlan( xml )
214
+ return false if mode_changed?
215
+ upd_untagged_vlan( xml )
216
+ end
217
+
218
+ def upd_untagged_vlan( xml )
219
+ self.class.change_untagged_vlan( self, xml )
220
+ end
221
+
222
+ end
223
+
224
+ ##### ---------------------------------------------------------------
225
+ ##### Class methods for handling state-transitions between
226
+ ##### configurations (tagged/untagged)
227
+ ##### ---------------------------------------------------------------
228
+
229
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
230
+
231
+ # creating some class definitions ...
232
+ # this is a bit complicated because we need to handle port-mode
233
+ # change transitions; basically dealing with the fact that
234
+ # trunk ports use 'native-vlan-id' and access ports have a
235
+ # vlan member definition; i.e. they don't use native-vlan-id, ugh.
236
+ # Rather than doing all this logic as if/then/else statements,
237
+ # I've opted to using a proc jump-table technique. Lessons
238
+ # learned from lots of embedded systems programming :-)
239
+
240
+ def self.init_jump_table
241
+
242
+ # auto-hash table, majik!
243
+ hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
244
+
245
+ # ------------------------------------------------------------------
246
+ # - jump table for handling various untagged vlan change use-cases
247
+ # ------------------------------------------------------------------
248
+ # There are three criteria for selection:
249
+ # | is_trunk | will_trunk | no_untg |
250
+ # ------------------------------------------------------------------
251
+
252
+ # - will not have untagged vlan
253
+ hash[false][false][true] = self.method(:ac_ac_nountg)
254
+ hash[false][true][true] = self.method(:ac_tr_nountg)
255
+ hash[true][false][true] = self.method(:tr_ac_nountg)
256
+ hash[true][true][true] = self.method(:tr_tr_nountg)
257
+
258
+ # - will have untagged vlan
259
+ hash[false][false][false] = self.method(:ac_ac_untg)
260
+ hash[false][true][false] = self.method(:ac_tr_untg)
261
+ hash[true][false][false] = self.method(:tr_ac_untg)
262
+ hash[true][true][false] = self.method(:tr_tr_untg)
263
+
264
+ hash
265
+ end
266
+
267
+ ### invoke the correct method from the jump table
268
+ ### based on the three criteria to select the action
269
+
270
+ def self.change_untagged_vlan( this, xml )
271
+ @@ez_l2_jmptbl ||= init_jump_table
272
+ proc = @@ez_l2_jmptbl[this.is_trunk?][this.should_trunk?][this.should[:untagged_vlan].nil?]
273
+ proc.call( this, xml )
274
+ end
275
+
276
+ ### -------------------------------------------------------------
277
+ ### The following are all the change transition functions for
278
+ ### each of the use-cases
279
+ ### -------------------------------------------------------------
280
+
281
+ def self.ac_ac_nountg( this, xml )
282
+ this._xml_rm_ac_untagged_vlan( xml )
283
+ end
284
+
285
+ def self.ac_tr_nountg( this, xml )
286
+ unless (untg_vlan = this.has[:untagged_vlan]).nil?
287
+ this._xml_rm_ac_untagged_vlan( xml )
288
+ end
289
+ end
290
+
291
+ def self.tr_ac_nountg( this, xml )
292
+ this._delete_native_vlan_id( xml )
293
+ this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]
294
+ end
295
+
296
+ def self.tr_tr_nountg( this, xml )
297
+ this._delete_native_vlan_id( xml )
298
+ end
299
+
300
+ ## ----------------------------------------------------------------
301
+ ## transition where port WILL-HAVE untagged-vlan
302
+ ## ----------------------------------------------------------------
303
+
304
+ def self.ac_ac_untg( this, xml )
305
+ this._xml_rm_ac_untagged_vlan( xml )
306
+ xml.vlan {
307
+ xml.members this.should[:untagged_vlan]
308
+ }
309
+ end
310
+
311
+ def self.ac_tr_untg( this, xml )
312
+ # move untagged vlan to native-vlan-id ...
313
+ was_untg_vlan = this.has[:untagged_vlan]
314
+ this._set_native_vlan_id( xml, this.should[:untagged_vlan] )
315
+ this._xml_rm_ac_untagged_vlan( xml ) if was_untg_vlan
316
+ end
317
+
318
+ def self.tr_ac_untg( this, xml )
319
+ this._delete_native_vlan_id( xml )
320
+ this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]
321
+ xml.vlan { xml.members this.should[:untagged_vlan] }
322
+ end
323
+
324
+ def self.tr_tr_untg( this, xml )
325
+ this._set_native_vlan_id( xml, this.should[:untagged_vlan] )
326
+ end
327
+ end
328
+
329
+ ##### ---------------------------------------------------------------
330
+ ##### Provider collection methods
331
+ ##### ---------------------------------------------------------------
332
+
333
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
334
+
335
+ def build_list
336
+ begin
337
+ got = @ndev.rpc.get_ethernet_switching_interface_information( :brief => true)
338
+ rescue => e
339
+ # in this case, no ethernet-switching is enabled so return empty list
340
+ return []
341
+ end
342
+ got.xpath('//l2iff-interface-name').collect{ |ifn| ifn.text.split('.')[0] }
343
+ end
344
+
345
+ def build_catalog
346
+ @catalog = {}
347
+ return @catalog if list!.empty?
348
+
349
+ list.each do |ifs_name|
350
+ @ndev.rpc.get_configuration{ |xml|
351
+ xml.interfaces {
352
+ xml_at_element_top( xml, ifs_name )
353
+ }
354
+ }.xpath('interfaces/interface').each do |ifs_xml|
355
+ @catalog[ifs_name] = {}
356
+ unit = xml_get_has_xml( ifs_xml )
357
+ xml_read_parser( unit, @catalog[ifs_name] )
358
+ end
359
+ end
360
+
361
+ @catalog
362
+ end
363
+
364
+ end
365
+
366
+ ##### ---------------------------------------------------------------
367
+ ##### !!!!! PRIVATE METHODS !!!!
368
+ ##### ---------------------------------------------------------------
369
+
370
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
371
+ private
372
+
373
+ def _get_eth_port_vlans_h( ifs_name )
374
+
375
+ got = @ndev.rpc.get_ethernet_switching_interface_information(:interface_name => ifs_name)
376
+ ret_h = {:untagged => nil, :tagged => Set.new }
377
+ got.xpath('//l2ng-l2ald-iff-interface-entry').each do |vlan|
378
+ # one of the node-set elements (the first one?) contains the interface name.
379
+ # this doesn't have any VLAN information, so skip it.
380
+ next if vlan.xpath('l2iff-interface-name')
381
+
382
+ vlan_name = vlan.xpath('l2iff-interface-vlan-name').text.strip
383
+ tgdy = vlan.xpath('l2iff-interface-vlan-member-tagness').text.strip
384
+ if tgdy == 'untagged'
385
+ ret_h[:untagged] = vlan_name
386
+ else
387
+ ret_h[:tagged] << vlan_name
388
+ end
389
+ end
390
+ ret_h
391
+ end
392
+
393
+ end
394
+
395
+
396
+ ### ---------------------------------------------------------------
397
+ ### [edit vlans] - for interfaces configured here ...
398
+ ### ---------------------------------------------------------------
399
+
400
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
401
+
402
+ def _xml_edit_under_vlans( xml )
403
+ Nokogiri::XML::Builder.with( xml.doc.root ) do |dot|
404
+ dot.vlans {
405
+ return dot
406
+ }
407
+ end
408
+ end
409
+
410
+ def _xml_rm_under_vlans( xml, vlans )
411
+ if vlans.any?
412
+ at_vlans = _xml_edit_under_vlans( xml )
413
+ vlans.each do |vlan_name|
414
+ Nokogiri::XML::Builder.with( at_vlans.parent ) do |this|
415
+ this.vlan {
416
+ this.name vlan_name
417
+ this.interface( Netconf::JunosConfig::DELETE ) { this.name @name }
418
+ }
419
+ end
420
+ end
421
+ end
422
+ end
423
+
424
+ def _xml_rm_ac_untagged_vlan( xml )
425
+ if @under_vlans.empty?
426
+ xml.vlan Netconf::JunosConfig::DELETE
427
+ else
428
+ _xml_rm_under_vlans( xml, [ @has[:untagged_vlan ] ] )
429
+ @under_vlans = []
430
+ end
431
+ end
432
+
433
+ def _xml_rm_these_vlans( xml, vlans )
434
+ if @under_vlans.empty?
435
+ xml.vlan( Netconf::JunosConfig::DELETE )
436
+ else
437
+ # could be a mix between [edit vlans] and [edit interfaces] ...
438
+ v_has = vlans.to_set
439
+ del_under_vlans = v_has & @under_vlans
440
+ _xml_rm_under_vlans( xml, del_under_vlans )
441
+ if v_has ^ @under_vlans
442
+ xml.vlan( Netconf::JunosConfig::DELETE )
443
+ end
444
+ @under_vlans = []
445
+ end
446
+ end
447
+
448
+ end
449
+
450
+
451
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
452
+
453
+ def _vlan_name_to_tag_id( vlan_name )
454
+ tag_id = @ndev.rpc.get_configuration { |xml|
455
+ xml.vlans { xml.vlan { xml.name vlan_name }}
456
+ }.xpath('//vlan-id').text.chomp
457
+
458
+ raise ArgumentError, "VLAN '#{vlan_name}' not found" if tag_id.empty?
459
+ return tag_id
460
+ end
461
+
462
+ def _vlan_tag_id_to_name( tag_id, my_hash )
463
+ # get the candidate configuration for each VLAN named in tagged_vlans and
464
+ # then map it to the corresponding vlan-id. this is not very effecient, but
465
+ # at present there is no other way without getting into a cache mech.
466
+ vlan_name = @ndev.rpc.get_configuration { |xml|
467
+ xml.vlans {
468
+ my_hash[:tagged_vlans].each do |v_name|
469
+ xml.vlan {
470
+ xml.name v_name
471
+ xml.send(:'vlan-id')
472
+ }
473
+ end
474
+ }
475
+ }.xpath("//vlan[vlan-id = '#{tag_id}']/name").text.chomp
476
+
477
+ raise ArgumentError, "VLAN-ID '#{tag_id}' not found" if vlan_name.empty?
478
+ return vlan_name
479
+ end
480
+
481
+ end
482
+
483
+ class Junos::Ez::L2ports::Provider::VLAN_L2NG
484
+ def _at_native_vlan_id( xml )
485
+
486
+ ifd
487
+ end
488
+
489
+ def _delete_native_vlan_id( xml )
490
+ Nokogiri::XML::Builder.with( @ifd ) do |dot|
491
+ dot.send :'native-vlan-id', Netconf::JunosConfig::DELETE
492
+ end
493
+ return true
494
+ end
495
+
496
+ def _set_native_vlan_id( xml, vlan_name )
497
+ Nokogiri::XML::Builder.with( @ifd ) do |dot|
498
+ dot.send :'native-vlan-id', _vlan_name_to_tag_id( vlan_name )
499
+ end
500
+ return true
501
+ end
502
+ end