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