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