Class: Junos::Ez::L2ports::Provider::BRIDGE_DOMAIN

Inherits:
Junos::Ez::L2ports::Provider show all
Defined in:
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb,
lib/junos-ez/l2_ports/bridge_domain.rb

Overview


edit vlans
  • for interfaces configured here …


Instance Attribute Summary

Attributes inherited from Provider::Parent

#catalog, #has, #list, #name, #ndev, #parent, #properties, #providers, #should

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Junos::Ez::L2ports::Provider

#is_trunk?, #mode_changed?, #should_trunk?, #xml_change__active

Methods inherited from Provider::Parent

#[], #[]=, #activate!, #active?, #catalog!, #create, #create!, #create_from_hash!, #create_from_yaml!, #deactivate!, #delete!, #each, #exists?, #init_has, #initialize, #is_new?, #is_provider?, #list!, #name_decorated, #need_write?, #read!, #rename!, #reorder!, #select, #to_h, #to_h_expanded, #to_yaml, #with, #write!, #xml_at_edit, #xml_change__active, #xml_change__exist, #xml_change_admin, #xml_config_read!, #xml_element_newname, #xml_on_create

Constructor Details

This class inherits a constructor from Junos::Ez::Provider::Parent

Class Method Details

.ac_ac_nountg(this, xml) ⇒ Object


The following are all the change transition functions for each of the use-cases




286
287
288
289
290
291
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 286

def self.ac_ac_nountg( this, xml )
   #NetdevJunos::Log.debug "ac_ac_nountg"
   # @@@ a port *MUST* be assigned to a vlan in access mode on MX.
   # @@@ generate an error!
   raise Junos::Ez::NoProviderError, "a port *MUST* be assigned to a vlan in access mode on MX."
end

.ac_ac_untg(this, xml) ⇒ Object


transition where port WILL-HAVE untagged-vlan




311
312
313
314
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 311

def self.ac_ac_untg( this, xml )
  vlan_id = this._vlan_name_to_tag_id( this.should[:untagged_vlan] )
  xml.send :'vlan-id', vlan_id 
end

.ac_tr_nountg(this, xml) ⇒ Object



293
294
295
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 293

def self.ac_tr_nountg( this, xml ) 
  #no action needed handled already
end

.ac_tr_untg(this, xml) ⇒ Object



316
317
318
319
320
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 316

def self.ac_tr_untg( this, xml )    
  was_untg_vlan = this.has[:untagged_vlan]
  this._set_native_vlan_id( xml, this.should[:untagged_vlan] )
  this._xml_rm_ac_untagged_vlan( xml ) if was_untg_vlan   
end

.change_untagged_vlan(this, xml) ⇒ Object

invoke the correct method from the jump table based on the three criteria to select the action



275
276
277
278
279
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 275

def self.change_untagged_vlan( this, xml )
  @@ez_l2_jmptbl ||= init_jump_table 
  proc = @@ez_l2_jmptbl[this.is_trunk?][this.should_trunk?][this.should[:untagged_vlan].nil?]
  proc.call( this, xml )
end

.init_jump_tableObject

creating some class definitions … this is a bit complicated because we need to handle port-mode change transitions; basically dealing with the fact that trunk ports use ‘native-vlan-id’ and access ports have a vlan member definition; i.e. they don’t use native-vlan-id, ugh. Rather than doing all this logic as if/then/else statements, I’ve opted to using a proc jump-table technique. Lessons learned from lots of embedded systems programming :-)



245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 245

def self.init_jump_table
  
  # auto-hash table, majik!
  hash = Hash.new(&(p=lambda{|h,k| h[k] = Hash.new(&p)}))
  
  # ------------------------------------------------------------------
  # -   jump table for handling various untagged vlan change use-cases      
  # ------------------------------------------------------------------      
  # There are three criteria for selection:  
  # | is_trunk | will_trunk | no_untg |
  # ------------------------------------------------------------------
  
  # - will not have untagged vlan 
  hash[false][false][true] = self.method(:ac_ac_nountg)
  hash[false][true][true] = self.method(:ac_tr_nountg)
  hash[true][false][true] = self.method(:tr_ac_nountg)
  hash[true][true][true] = self.method(:tr_tr_nountg)
  
  # - will have untagged vlan 
  hash[false][false][false] = self.method(:ac_ac_untg)
  hash[false][true][false] = self.method(:ac_tr_untg)
  hash[true][false][false] = self.method(:tr_ac_untg)
  hash[true][true][false] = self.method(:tr_tr_untg)
  
  hash
end

.tr_ac_nountg(this, xml) ⇒ Object



297
298
299
300
301
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 297

def self.tr_ac_nountg( this, xml )
   # @@@ a port *MUST* be assigned to a vlan in access mode on MX.
   # @@@ generate an error!
   raise Junos::Ez::NoProviderError, "a port *MUST* be assigned to vlan in access mode on MX"
end

.tr_ac_untg(this, xml) ⇒ Object



322
323
324
325
326
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 322

def self.tr_ac_untg( this, xml ) 
  this._delete_native_vlan_id( xml )
  vlan_id = this._vlan_name_to_tag_id( this.should[:untagged_vlan] )
  xml.send( :'vlan-id', vlan_id )
end

.tr_tr_nountg(this, xml) ⇒ Object



303
304
305
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 303

def self.tr_tr_nountg( this, xml )
  this._delete_native_vlan_id( xml )  
end

.tr_tr_untg(this, xml) ⇒ Object



328
329
330
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 328

def self.tr_tr_untg( this, xml )
  this._set_native_vlan_id(xml, this.should[:untagged_vlan])
end

Instance Method Details

#_at_native_vlan_id(xml) ⇒ Object



479
480
481
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 479

def _at_native_vlan_id( xml )
  ifd
end

#_delete_native_vlan_id(xml) ⇒ Object



483
484
485
486
487
488
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 483

def _delete_native_vlan_id( xml )
  Nokogiri::XML::Builder.with( @ifd ) do |dot|
    dot.send :'native-vlan-id', Netconf::JunosConfig::DELETE
  end
  return true
end

#_set_native_vlan_id(xml, vlan_name) ⇒ Object



490
491
492
493
494
495
496
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 490

def _set_native_vlan_id( xml, vlan_name )
  Nokogiri::XML::Builder.with( @ifd ) do |dot|
    dot.send :'native-vlan-id', _vlan_name_to_tag_id( vlan_name )
    xml.send( :'vlan-id-list', _vlan_name_to_tag_id( vlan_name) )
  end    
  return true
end

#_vlan_name_to_tag_id(vlan_name) ⇒ Object

Raises:

  • (ArgumentError)


458
459
460
461
462
463
464
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 458

def _vlan_name_to_tag_id( vlan_name )
  tag_id = @ndev.rpc.get_configuration { |xml|
    xml.send(:'bridge-domains') { xml.domain { xml.name vlan_name }}
  }.xpath('//vlan-id').text.chomp
  raise ArgumentError, "VLAN '#{vlan_name}' not found" if tag_id.empty?
  return tag_id
end

#_vlan_tag_id_to_name(vlan_id) ⇒ Object

Raises:

  • (ArgumentError)


467
468
469
470
471
472
473
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 467

def _vlan_tag_id_to_name( vlan_id )
  tag_name = @ndev.rpc.get_configuration { |xml|
    xml.send(:'bridge-domains') { xml.domain { xml.send(:'vlan-id', vlan_id)}}
  }.xpath('//name').text.chomp
  raise ArgumentError, "VLAN '#{vlan_id}' not found" if tag_name.empty?
  return tag_name
end

#_xml_edit_under_vlans(xml) ⇒ Object



407
408
409
410
411
412
413
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 407

def _xml_edit_under_vlans( xml ) 
  Nokogiri::XML::Builder.with( xml.doc.root ) do |dot|
    dot.send(:'vlan-id'){
      return dot
    }
  end      
end

#_xml_rm_ac_untagged_vlan(xml) ⇒ Object



429
430
431
432
433
434
435
436
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 429

def _xml_rm_ac_untagged_vlan( xml )
  if @under_vlans.empty?
    xml.send :'vlan-id', Netconf::JunosConfig::DELETE    
  else
    _xml_rm_under_vlans( xml, [ @has[:untagged_vlan ] ] )
    @under_vlans = []    
  end
end

#_xml_rm_these_vlans(xml, vlans) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 438

def _xml_rm_these_vlans( xml, vlans )
  if @under_vlans.empty?
    xml.send :'vlan-id', ( Netconf::JunosConfig::DELETE ) 
  else
    # could be a mix between [edit vlans] and [edit interfaces] ...
    v_has = vlans.to_set
    del_under_vlans = v_has & @under_vlans
    _xml_rm_under_vlans( xml, del_under_vlans )
    if v_has ^ @under_vlans
      xml.send :'vlan-id', ( Netconf::JunosConfig::DELETE ) 
    end
    @under_vlans = []        
  end
end

#_xml_rm_under_vlans(xml, vlans) ⇒ Object



415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 415

def _xml_rm_under_vlans( xml, vlans )
  if vlans.any?
    at_vlans = _xml_edit_under_vlans( xml )
    vlans.each do |vlan_id|
      Nokogiri::XML::Builder.with( at_vlans.parent ) do |this|
        this.domain {
          this.vlan_id vlan_id
          this.interface( Netconf::JunosConfig::DELETE ) { this.name @name }
        }
      end
    end
  end    
end

#build_catalogObject



350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 350

def build_catalog
  @catalog = {}    
  return @catalog if list!.empty?
  list.each do |ifs_name|
    @ndev.rpc.get_configuration{ |xml|
      xml.interfaces {
        xml_at_element_top( xml, ifs_name )
      }
    }.xpath('interfaces/interface').each do |ifs_xml|
      @catalog[ifs_name] = {}
      unit = xml_get_has_xml( ifs_xml )
      xml_read_parser( unit, @catalog[ifs_name] )
    end
  end    
  
  @catalog
end

#build_listObject



340
341
342
343
344
345
346
347
348
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 340

def build_list
  begin
    got = @ndev.rpc.get_bridge_instance_information( :brief => true)
  rescue => e
    # in this case, no ethernet-switching is enabled so return empty list
    return []
  end   
  got.xpath('//l2iff-interface-name').collect{ |ifn| ifn.text.split('.')[0] }
end

#set_ifd_trunking(xml, should_trunk) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 166

def set_ifd_trunking( xml, should_trunk )
   par = xml.instance_variable_get(:@parent)     
   Nokogiri::XML::Builder.with( par.at_xpath( 'ancestor::interface' )) do |dot|
     if should_trunk
       dot.send( :'flexible-vlan-tagging' )
       dot.send( :'encapsulation', 'flexible-ethernet-services' )
     else
       dot.send( :'flexible-vlan-tagging', Netconf::JunosConfig::DELETE )
       dot.send( :'encapsulation', Netconf::JunosConfig::DELETE )
     end
   end       
end

#upd_tagged_vlans(xml) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 188

def upd_tagged_vlans( xml )
  return false unless should_trunk?
  
  @should[:tagged_vlans] = @should[:tagged_vlans].to_set if @should[:tagged_vlans].kind_of? Array
  @has[:tagged_vlans] = @has[:tagged_vlans].to_set if @has[:tagged_vlans].kind_of? Array    
  
  v_should = @should[:tagged_vlans] || Set.new    
  v_has = @has[:tagged_vlans] || Set.new  
    
  del = v_has - v_should
  add = v_should - v_has 
  
  del_under_vlans = del & @under_vlans    
  unless del_under_vlans.empty?
    del = del ^ @under_vlans
    _xml_rm_under_vlans( xml, del_under_vlans )
    @under_vlans = []
  end
  
  if add or del
    del.each{|v| xml.send(:'vlan-id-list', _vlan_name_to_tag_id( v ), Netconf::JunosConfig::DELETE)}
    add.each{|v| xml.send( :'vlan-id-list', _vlan_name_to_tag_id(v) )}
  end
  return true    
end

#upd_untagged_vlan(xml) ⇒ Object



223
224
225
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 223

def upd_untagged_vlan( xml )
  self.class.change_untagged_vlan( self, xml )
end

#xml_at_element_top(xml, name) ⇒ Object

set the edit anchor inside bridge-domains stanza we will need to ‘up-out’ when making changes to the unit information, like description



19
20
21
22
23
24
25
26
27
28
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 19

def xml_at_element_top( xml, name )
  xml.interface {
    xml.name name
    xml.send(:'native-vlan-id')
    xml.unit { 
      xml.name '0'
      return xml
    }
  }  
end

#xml_at_here(xml) ⇒ Object


XML property writers




116
117
118
119
120
121
122
123
124
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 116

def xml_at_here( xml )
  @ifd = xml.instance_variable_get(:@parent).at_xpath('ancestor::interface')
  @ifd.xpath('//native-vlan-id').remove      ## remove the element from the get-config
  xml.family {
    xml.send(:'bridge') {
      return xml
    }
  }
end

#xml_at_topObject


XML top placement




7
8
9
10
11
12
13
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 7

def xml_at_top
  Nokogiri::XML::Builder.new {|xml| xml.configuration {
    xml.interfaces {
      return xml_at_element_top( xml, @name )
    }
  }}
end

#xml_build_change(nop = nil) ⇒ Object



126
127
128
129
130
131
132
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 126

def xml_build_change( nop = nil )
  @under_vlans ||= []       # handles case for create'd port
  if mode_changed?
    @should[:untagged_vlan] ||= @has[:untagged_vlan]    
  end
  super xml_at_here( xml_at_top )
end

#xml_change_description(xml) ⇒ Object

overload default method since we need to “up-out” of the



140
141
142
143
144
145
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 140

def xml_change_description( xml )
  unit = xml.parent.xpath('ancestor::unit')[0]
  Nokogiri::XML::Builder.with( unit ){ |x| 
    xml_set_or_delete( x, 'description', @should[:description] )
  }
end

#xml_change_tagged_vlans(xml) ⇒ Object


:tagged_vlans




183
184
185
186
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 183

def xml_change_tagged_vlans( xml )  
  return false if mode_changed?  
  upd_tagged_vlans( xml )
end

#xml_change_untagged_vlan(xml) ⇒ Object


:untagged_vlan




218
219
220
221
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 218

def xml_change_untagged_vlan( xml ) 
  return false if mode_changed?         
  upd_untagged_vlan( xml )
end

#xml_change_vlan_tagging(xml) ⇒ Object


:vlan_tagging




151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 151

def xml_change_vlan_tagging( xml )
  port_mode = should_trunk? ? 'trunk' : 'access'
  xml.send(:'interface-mode', port_mode )

  # when the vlan_tagging value changes then this method
  # will trigger updates to the untagged_vlan and tagged_vlans
  # resource values as well.
  # !!! DO NOT SWAP THIS ORDER untagged processing *MUST* BE FIRST!
  
  upd_untagged_vlan( xml )
  upd_tagged_vlans( xml ) 
      
  return true
end

#xml_get_has_xml(xml) ⇒ Object


XML property readers




34
35
36
37
38
39
40
41
42
43
44
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 34

def xml_get_has_xml( xml ) 
  # second unit contains the family/bridge-domains stanza
  got = xml.xpath('//unit')[0]
  # if this resource doesn't exist we need to default some 
  # values into has/should variables
  unless got
    @has[:vlan_tagging] = false
    @should = @has.clone
  end
  got
end

#xml_on_delete(xml) ⇒ Object

overload the xml_on_delete method since we may need to do some cleanup work in the [edit vlans] stanza



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 98

def xml_on_delete( xml )
  @ifd = xml.instance_variable_get(:@parent).at_xpath('ancestor::interface')
  @ifd.xpath('//native-vlan-id').remove      ## remove the element from the get-config    
  ## need to add check if any native-vlan-id is present or not (untagged vlan)#####
 if is_trunk? and @ifd.xpath('//native-vlan-id')
    _delete_native_vlan_id( xml )
 end
    
  return unless @under_vlans
  return if @under_vlans.empty?

  _xml_rm_under_vlans( xml, @under_vlans )
end

#xml_read_parser(as_xml, as_hash) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/junos-ez/l2_ports/bridge_domain.rb', line 46

def xml_read_parser( as_xml, as_hash )
  ## reading is anchored at the [... unit 0 ...] level
  set_has_status( as_xml, as_hash )  
  
  xml_when_item(as_xml.xpath('description')){|i| as_hash[:description] = i.text}

  f_eth = as_xml.xpath('family/bridge')        
  as_hash[:vlan_tagging] = f_eth.xpath('interface-mode').text.chomp == 'trunk' 
  
  # obtain a copy of the running state, this is needed in case the config
  # is located under the [edit vlans] stanza vs. [edit interfaces]
  
  ifs_name = @name || as_xml.xpath('ancestor::interface/name').text.strip
  eth_port_vlans = _get_eth_port_vlans_h( ifs_name )
  @under_vlans = []
  
  # --- access port        
  
  if as_hash[:vlan_tagging] == false
    xml_when_item(f_eth.xpath('domain/vlan-id')){ |i| as_hash[:untagged_vlan] = i.text.chomp }
    unless as_hash[:untagged_vlan]
      as_hash[:untagged_vlan] = eth_port_vlans[:untagged]
      @under_vlans << eth_port_vlans[:untagged]
    end
    return
  end
  
  # --- trunk port    
  as_hash[:untagged_vlan] ||= eth_port_vlans[:untagged]
  as_hash[:tagged_vlans] = f_eth.xpath('//bridge/vlan-id-list').collect { |v| v.text.chomp }.to_set
  (eth_port_vlans[:tagged] - as_hash[:tagged_vlans]).each do |vlan|
    as_hash[:tagged_vlans] << vlan
   @under_vlans << vlan
  end
  # native-vlan-id is set at the interface level, and is the VLAN-ID, not the vlan
  # name.  So we need to do a bit of translating here.  The *ASSUMPTION* is that the
  # native-vlan-id value is a given VLAN in the tagged_vlan list.  So we will use 
  # that list to do the reverse lookup on the tag-id => name
  as_hash[:tagged_vlans]= as_hash[:tagged_vlans].collect {|x| _vlan_tag_id_to_name(x)} 
  xml_when_item(f_eth.xpath('ancestor::interface/native-vlan-id')){ |i|
    as_hash[:untagged_vlan] = _vlan_tag_id_to_name( i.text.chomp) 
  }
  as_hash[:tagged_vlans].delete(as_hash[:untagged_vlan])
end