junos-ez-stdlib 0.1.2 → 1.0.3
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.
- checksums.yaml +6 -14
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +60 -19
- data/Gemfile +7 -0
- data/README.md +41 -30
- data/Rakefile +6 -0
- data/SUGGESTION-BOX/README.md +32 -0
- data/docs/Providers/Group.md +61 -0
- data/docs/Providers/L2ports.md +1 -1
- data/docs/Providers/LAGports.md +57 -0
- data/docs/Providers/Vlans.md +1 -1
- data/examples/config/config_file.rb +0 -0
- data/examples/config/config_template_object.rb +0 -0
- data/examples/config/config_template_simple.rb +0 -0
- data/examples/config/load_sample.conf +129 -0
- data/examples/config/load_sample.set +3 -0
- data/examples/config/load_template_main.conf +7 -0
- data/examples/config/load_template_object.conf +7 -0
- data/examples/config/multi_config.rb +0 -0
- data/examples/fs_utils.rb +0 -0
- data/examples/lag_port.rb +27 -0
- data/examples/re_upgrade.rb +0 -0
- data/examples/re_utils.rb +0 -0
- data/examples/simple.rb +0 -1
- data/examples/st_hosts.rb +0 -0
- data/examples/user.rb +0 -0
- data/examples/vlans.rb +4 -4
- data/junos-ez-stdlib.gemspec +25 -14
- data/lib/junos-ez/exceptions.rb +0 -0
- data/lib/junos-ez/facts.rb +5 -7
- data/lib/junos-ez/facts/chassis.rb +6 -0
- data/lib/junos-ez/facts/ifd_style.rb +6 -3
- data/lib/junos-ez/facts/personality.rb +6 -6
- data/lib/junos-ez/facts/switch_style.rb +11 -2
- data/lib/junos-ez/facts/version.rb +24 -9
- data/lib/junos-ez/group.rb +206 -0
- data/lib/junos-ez/ip_ports.rb +0 -0
- data/lib/junos-ez/ip_ports/classic.rb +2 -2
- data/lib/junos-ez/l1_ports.rb +0 -0
- data/lib/junos-ez/l1_ports/classic.rb +0 -0
- data/lib/junos-ez/l1_ports/switch.rb +0 -0
- data/lib/junos-ez/l2_ports.rb +18 -9
- data/lib/junos-ez/l2_ports/bridge_domain.rb +499 -0
- data/lib/junos-ez/l2_ports/vlan.rb +3 -3
- data/lib/junos-ez/l2_ports/vlan_l2ng.rb +502 -0
- data/lib/junos-ez/lag_ports.rb +268 -0
- data/lib/junos-ez/provider.rb +4 -8
- data/lib/junos-ez/stdlib.rb +2 -0
- data/lib/junos-ez/system.rb +0 -0
- data/lib/junos-ez/system/users.rb +5 -7
- data/lib/junos-ez/utils/config.rb +0 -0
- data/lib/junos-ez/utils/fs.rb +0 -0
- data/lib/junos-ez/utils/re.rb +0 -0
- data/lib/junos-ez/version.rb +4 -1
- data/lib/junos-ez/vlans.rb +4 -1
- data/lib/junos-ez/vlans/bridge_domain.rb +7 -3
- data/lib/junos-ez/vlans/vlan.rb +4 -3
- data/lib/junos-ez/vlans/vlan_l2ng.rb +126 -0
- metadata +142 -64
@@ -4,7 +4,11 @@ Junos::Ez::Facts::Keeper.define( :version ) do |ndev, facts|
|
|
4
4
|
|
5
5
|
case f_persona
|
6
6
|
when :MX
|
7
|
-
|
7
|
+
begin
|
8
|
+
swver = ndev.rpc.command "show version invoke-on all-routing-engines"
|
9
|
+
rescue Netconf::RpcError
|
10
|
+
swver = ndev.rpc.command "show version"
|
11
|
+
end
|
8
12
|
when :SWITCH
|
9
13
|
## most EX switches support the virtual-chassis feature, so the 'all-members' option would be valid
|
10
14
|
## in some products, this options is not valid (i.e. not vc-capable. so we're going to try for vc, and if that
|
@@ -26,18 +30,29 @@ Junos::Ez::Facts::Keeper.define( :version ) do |ndev, facts|
|
|
26
30
|
swver_infos = swver.xpath('//software-information')
|
27
31
|
swver_infos.each do |re_sw|
|
28
32
|
re_name = re_sw.xpath('preceding-sibling::re-name').text.upcase
|
29
|
-
re_sw.xpath('package-information[1]/comment').text =~ /\[(.*)\]/
|
30
33
|
ver_key = ('version_' + re_name).to_sym
|
31
|
-
|
34
|
+
|
35
|
+
if re_sw.at_xpath('//junos-version')
|
36
|
+
facts[ver_key] = re_sw.xpath('//junos-version').text
|
37
|
+
else
|
38
|
+
re_sw.xpath('package-information[1]/comment').text =~ /\[(.*)\]/
|
39
|
+
facts[ver_key] = $1
|
40
|
+
end
|
32
41
|
end
|
33
42
|
master_id = f_master
|
34
|
-
|
35
|
-
facts[
|
36
|
-
|
43
|
+
unless master_id.nil?
|
44
|
+
facts[:version] =
|
45
|
+
facts[("version_" + "RE" + master_id).to_sym] ||
|
46
|
+
facts[('version_' + "FPC" + master_id).to_sym]
|
47
|
+
end
|
37
48
|
else
|
38
|
-
|
39
|
-
|
40
|
-
|
49
|
+
if swver.at_xpath('//junos-version')
|
50
|
+
facts[:version] = swver.xpath('//junos-version').text
|
51
|
+
else
|
52
|
+
junos = swver.xpath('//package-information[name = "junos"]/comment').text
|
53
|
+
junos =~ /\[(.*)\]/
|
54
|
+
facts[:version] = $1
|
55
|
+
end
|
41
56
|
end
|
42
57
|
|
43
58
|
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require "junos-ez/provider"
|
2
|
+
|
3
|
+
module Junos::Ez::Group
|
4
|
+
|
5
|
+
PROPERTIES = [
|
6
|
+
:format, # [:set, :text, :xml]
|
7
|
+
:path, # Configuration file path
|
8
|
+
]
|
9
|
+
|
10
|
+
def self.Provider( ndev, varsym )
|
11
|
+
newbie = Junos::Ez::Group::Provider::new( ndev )
|
12
|
+
newbie.properties = Junos::Ez::Provider::PROPERTIES + PROPERTIES
|
13
|
+
Junos::Ez::Provider.attach_instance_variable( ndev, varsym, newbie )
|
14
|
+
end
|
15
|
+
|
16
|
+
class Provider < Junos::Ez::Provider::Parent
|
17
|
+
# common parenting goes here ... if we were to
|
18
|
+
# subclass the objects ... not doing that now
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class Junos::Ez::Group::Provider
|
24
|
+
|
25
|
+
### ---------------------------------------------------------------
|
26
|
+
### XML top placement
|
27
|
+
### ---------------------------------------------------------------
|
28
|
+
|
29
|
+
def xml_at_top
|
30
|
+
xml = Nokogiri::XML::Builder.new {|xml| xml.configuration {
|
31
|
+
xml.groups {
|
32
|
+
xml.name @name
|
33
|
+
return xml
|
34
|
+
}
|
35
|
+
}}
|
36
|
+
end
|
37
|
+
|
38
|
+
### ---------------------------------------------------------------
|
39
|
+
### XML property readers
|
40
|
+
### ---------------------------------------------------------------
|
41
|
+
|
42
|
+
def xml_get_has_xml( xml )
|
43
|
+
xml.xpath('//groups')[0]
|
44
|
+
end
|
45
|
+
|
46
|
+
def xml_read_parser( as_xml, as_hash )
|
47
|
+
set_has_status( as_xml, as_hash )
|
48
|
+
|
49
|
+
grp = as_xml.xpath('name').text
|
50
|
+
as_hash[:name] = grp unless grp.empty?
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
### ---------------------------------------------------------------
|
56
|
+
### XML property writers
|
57
|
+
### ---------------------------------------------------------------
|
58
|
+
|
59
|
+
def xml_change_path( xml )
|
60
|
+
end
|
61
|
+
|
62
|
+
def xml_change_format( xml )
|
63
|
+
end
|
64
|
+
|
65
|
+
### ---------------------------------------------------------------
|
66
|
+
### XML on-create
|
67
|
+
### ---------------------------------------------------------------
|
68
|
+
|
69
|
+
def xml_on_create( xml )
|
70
|
+
end
|
71
|
+
|
72
|
+
### ---------------------------------------------------------------
|
73
|
+
### XML on-delete
|
74
|
+
### ---------------------------------------------------------------
|
75
|
+
def xml_on_delete( xml )
|
76
|
+
end
|
77
|
+
|
78
|
+
def write_xml_config!( xml, opts = {} )
|
79
|
+
if (@should[:_exist] == true)
|
80
|
+
_load ( xml )
|
81
|
+
@should[:format] = 'xml' unless @should[:format]
|
82
|
+
begin
|
83
|
+
attr = {}
|
84
|
+
attr[:action] = 'replace'
|
85
|
+
attr[:format] = @should[:format].to_s
|
86
|
+
result = @ndev.rpc.load_configuration( @config.to_s, attr )
|
87
|
+
rescue Netconf::RpcError => e
|
88
|
+
errs = e.rsp.xpath('//rpc-error[error-severity = "error"]')
|
89
|
+
raise e unless errs.empty?
|
90
|
+
e.rsp
|
91
|
+
else
|
92
|
+
result
|
93
|
+
end
|
94
|
+
else
|
95
|
+
super(xml)
|
96
|
+
end
|
97
|
+
_apply_group
|
98
|
+
end
|
99
|
+
|
100
|
+
def write!
|
101
|
+
return nil if @should.empty?
|
102
|
+
|
103
|
+
@should[:_exist] ||= true
|
104
|
+
@should[:_active] ||= :true
|
105
|
+
# load the conifguration from file and apply under group
|
106
|
+
# hirerachy
|
107
|
+
rsp = write_xml_config!( xml_at_top.doc.root )
|
108
|
+
|
109
|
+
# copy the 'should' values into the 'has' values now that
|
110
|
+
# they've been written back to Junos
|
111
|
+
|
112
|
+
@has.merge! @should
|
113
|
+
@should.clear
|
114
|
+
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
##### ---------------------------------------------------------------
|
122
|
+
##### Provider collection methods
|
123
|
+
##### ---------------------------------------------------------------
|
124
|
+
|
125
|
+
class Junos::Ez::Group::Provider
|
126
|
+
|
127
|
+
def build_list
|
128
|
+
grp_cfgs = @ndev.rpc.get_configuration{|xml|
|
129
|
+
xml.send(:'groups')
|
130
|
+
}.xpath('groups/name').collect do |item|
|
131
|
+
item.text
|
132
|
+
end
|
133
|
+
return grp_cfgs
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_catalog
|
137
|
+
return @catalog if list!.empty?
|
138
|
+
list.each do |grp_name|
|
139
|
+
@ndev.rpc.get_configuration{ |xml|
|
140
|
+
xml.groups {
|
141
|
+
xml.name grp_name
|
142
|
+
}
|
143
|
+
}.xpath('groups').each do |as_xml|
|
144
|
+
@catalog[grp_name] = {}
|
145
|
+
xml_read_parser( as_xml, @catalog[grp_name] )
|
146
|
+
end
|
147
|
+
end
|
148
|
+
@catalog
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
##### ---------------------------------------------------------------
|
153
|
+
##### _PRIVATE methods
|
154
|
+
##### ---------------------------------------------------------------
|
155
|
+
|
156
|
+
class Junos::Ez::Group::Provider
|
157
|
+
|
158
|
+
def _load ( xml )
|
159
|
+
return @config = nil if ( @should[:_exist] == false )
|
160
|
+
admin = ''
|
161
|
+
if @should[:format].to_s == 'set'
|
162
|
+
@config = "\ndelete groups #{@name}\n" +
|
163
|
+
"edit groups #{@name}\n" +
|
164
|
+
File.read( @should[:path] )
|
165
|
+
admin = @should[:_active] == :false ? 'deactivate' : 'activate'
|
166
|
+
@config += "\nquit\n"
|
167
|
+
@config += "\n#{admin} groups #{@name}"
|
168
|
+
|
169
|
+
elsif @should[:format].to_s == 'text'
|
170
|
+
admin = @should[:_active] == :false ? 'inactive' : 'active'
|
171
|
+
admin += ": " unless admin.empty?
|
172
|
+
@config = "groups {\n#{admin} replace: #{@name} {\n" +
|
173
|
+
File.read( @should[:path] ) + "\n}\n}"
|
174
|
+
|
175
|
+
elsif @should[:format].to_s == 'xml'
|
176
|
+
xml.at_xpath('groups') << File.read( @should[:path])
|
177
|
+
@config = xml
|
178
|
+
end
|
179
|
+
return @config
|
180
|
+
end
|
181
|
+
|
182
|
+
def _apply_group
|
183
|
+
cfg = Netconf::JunosConfig.new(:TOP)
|
184
|
+
xml = cfg.doc
|
185
|
+
Nokogiri::XML::Builder.with( xml.at_xpath( 'configuration' )) do |dot|
|
186
|
+
if @config and @should[:_active] == :true
|
187
|
+
dot.send :'apply-groups', @name
|
188
|
+
else
|
189
|
+
dot.send :'apply-groups', @name, Netconf::JunosConfig::DELETE
|
190
|
+
end
|
191
|
+
end
|
192
|
+
begin
|
193
|
+
attr = {}
|
194
|
+
attr[:action] = 'replace'
|
195
|
+
attr[:format] = 'xml'
|
196
|
+
result = @ndev.rpc.load_configuration( xml, attr )
|
197
|
+
rescue Netconf::RpcError => e
|
198
|
+
errs = e.rsp.xpath('//rpc-error[error-severity = "error"]')
|
199
|
+
raise e unless errs.empty?
|
200
|
+
e.rsp
|
201
|
+
else
|
202
|
+
result
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
data/lib/junos-ez/ip_ports.rb
CHANGED
File without changes
|
@@ -175,9 +175,9 @@ class Junos::Ez::IPports::Provider::CLASSIC
|
|
175
175
|
|
176
176
|
xml_data = @ndev.rpc.get_interface_information(
|
177
177
|
:terse => true,
|
178
|
-
|
178
|
+
)
|
179
179
|
|
180
|
-
ifa_list = xml_data.xpath('logical-interface[normalize-space(address-family/address-family-name) = "inet"]')
|
180
|
+
ifa_list = xml_data.xpath('interface-information/logical-interface[normalize-space(address-family/address-family-name) = "inet"]')
|
181
181
|
|
182
182
|
end
|
183
183
|
|
data/lib/junos-ez/l1_ports.rb
CHANGED
File without changes
|
File without changes
|
File without changes
|
data/lib/junos-ez/l2_ports.rb
CHANGED
@@ -15,12 +15,12 @@ module Junos::Ez::L2ports
|
|
15
15
|
newbie = case ndev.fact( :switch_style )
|
16
16
|
when :VLAN
|
17
17
|
Junos::Ez::L2ports::Provider::VLAN.new( ndev )
|
18
|
-
when :
|
19
|
-
|
20
|
-
Junos::Ez::L2ports::Provider::VLAN_NG.new( ndev )
|
18
|
+
when :VLAN_L2NG
|
19
|
+
Junos::Ez::L2ports::Provider::VLAN_L2NG.new( ndev )
|
21
20
|
when :BRIDGE_DOMAIN
|
22
|
-
|
23
|
-
|
21
|
+
Junos::Ez::L2ports::Provider::BRIDGE_DOMAIN.new(ndev)
|
22
|
+
#raise ArgumentError, "under development"
|
23
|
+
# Junos::Ez::L2ports::Provider::BRIDGE_DOMAIN.new( ndev )
|
24
24
|
end
|
25
25
|
|
26
26
|
newbie.properties = Junos::Ez::Provider::PROPERTIES + PROPERTIES
|
@@ -44,14 +44,23 @@ module Junos::Ez::L2ports
|
|
44
44
|
@should[:vlan_tagging] != @has[:vlan_tagging]
|
45
45
|
end
|
46
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
|
+
|
47
58
|
end
|
48
59
|
|
49
60
|
end
|
50
61
|
|
51
62
|
require 'junos-ez/l2_ports/vlan'
|
52
|
-
|
53
|
-
require 'junos-ez/
|
54
|
-
require 'junos-ez/l2ports/bridge_domain' ... under development
|
55
|
-
=end
|
63
|
+
require 'junos-ez/l2_ports/vlan_l2ng'
|
64
|
+
require 'junos-ez/l2_ports/bridge_domain'
|
56
65
|
|
57
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
|