class Junos::Ez::L2ports::Provider::BRIDGE_DOMAIN


Class methods for handling state-transitions between configurations (tagged/untagged)



Provider collection methods



!!!!! PRIVATE METHODS !!!!


edit vlans
  • for interfaces configured here …


Public Class Methods

ac_ac_nountg( this, xml ) click to toggle source

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


# 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 ) click to toggle source

transition where port WILL-HAVE untagged-vlan


# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source

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

# 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_table() click to toggle source

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 :-)

# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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

Public Instance Methods

_at_native_vlan_id( xml ) click to toggle source
# File lib/junos-ez/l2_ports/bridge_domain.rb, line 479
def _at_native_vlan_id( xml )
  ifd
end
_delete_native_vlan_id( xml ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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_catalog() click to toggle source
# 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_list() click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source
# 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 ) click to toggle source

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

# 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 ) click to toggle source

XML property writers


# 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_top() click to toggle source

XML top placement


# 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 ) click to toggle source
# 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 ) click to toggle source

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

# 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 ) click to toggle source

:tagged_vlans


# 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 ) click to toggle source

:untagged_vlan


# 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 ) click to toggle source

:vlan_tagging


# 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 ) click to toggle source

XML property readers


# 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 ) click to toggle source

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

# 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 ) click to toggle source
# 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

Private Instance Methods

_get_eth_port_vlans_h( ifs_name ) click to toggle source
# File lib/junos-ez/l2_ports/bridge_domain.rb, line 377
def _get_eth_port_vlans_h( ifs_name )
  got = @ndev.rpc.get_bridge_instance_information(:interface => ifs_name)
  ret_h = {:untagged => nil, :tagged => Set.new }
  got.xpath('//l2ng-l2ald-iff-interface-entry').each do |vlan|
    # one of the node-set elements (the first one?) contains the interface name.
    # this doesn't have any VLAN information, so skip it.
    next if vlan.xpath('l2iff-interface-name')
    
    vlan_name = vlan.xpath('//l2rtb-bridge-vlan').text.strip 
    if vlan.xpath('//l2rtb-interface-vlan-member-tagness')
      tgdy = vlan.xpath('//l2rtb-interface-vlan-member-tagness').text.strip
      if tgdy == 'untagged'
        ret_h[:untagged] = vlan_name
      else
        ret_h[:tagged] << vlan_name
      end      
    else
      ret_h[:tagged]<<vlan_name       
    end         
  end
  ret_h
end