shopify-junos-ez-stdlib 1.0.0

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