class Junos::Ez::L2ports::Provider::VLAN_L2NG


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/vlan_l2ng.rb, line 281
def self.ac_ac_nountg( this, xml )
  this._xml_rm_ac_untagged_vlan( xml )
end
ac_ac_untg( this, xml ) click to toggle source

transition where port WILL-HAVE untagged-vlan


# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 304
def self.ac_ac_untg( this, xml )
  this._xml_rm_ac_untagged_vlan( xml )
  xml.vlan {
    xml.members this.should[:untagged_vlan]
  }            
end
ac_tr_nountg( this, xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 285
def self.ac_tr_nountg( this, xml )      
  unless (untg_vlan = this.has[:untagged_vlan]).nil?
    this._xml_rm_ac_untagged_vlan( xml )
  end
end
ac_tr_untg( this, xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 311
def self.ac_tr_untg( this, xml )      
  # move untagged vlan to native-vlan-id ...
  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/vlan_l2ng.rb, line 270
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/vlan_l2ng.rb, line 240
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/vlan_l2ng.rb, line 291
def self.tr_ac_nountg( this, xml )
  this._delete_native_vlan_id( xml )
  this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]    
end
tr_ac_untg( this, xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 318
def self.tr_ac_untg( this, xml )    
  this._delete_native_vlan_id( xml )
  this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]         
  xml.vlan { xml.members this.should[:untagged_vlan] }
end
tr_tr_nountg( this, xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 296
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/vlan_l2ng.rb, line 324
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/vlan_l2ng.rb, line 484
def _at_native_vlan_id( xml )

  ifd
end
_delete_native_vlan_id( xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 489
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/vlan_l2ng.rb, line 496
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 )
  end    
  return true
end
_vlan_name_to_tag_id( vlan_name ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 453
def _vlan_name_to_tag_id( vlan_name )
  tag_id = @ndev.rpc.get_configuration { |xml|
    xml.vlans { xml.vlan { 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( tag_id, my_hash ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 462
def _vlan_tag_id_to_name( tag_id, my_hash )
  # get the candidate configuration for each VLAN named in tagged_vlans and
  # then map it to the corresponding vlan-id.  this is not very effecient, but
  # at present there is no other way without getting into a cache mech.
  vlan_name = @ndev.rpc.get_configuration { |xml|
    xml.vlans {
      my_hash[:tagged_vlans].each do |v_name|
        xml.vlan { 
          xml.name v_name 
          xml.send(:'vlan-id')
        }
      end
    }
  }.xpath("//vlan[vlan-id = '#{tag_id}']/name").text.chomp    
  
  raise ArgumentError, "VLAN-ID '#{tag_id}' not found" if vlan_name.empty?
  return vlan_name
end
_xml_edit_under_vlans( xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 402
  def _xml_edit_under_vlans( xml )
  Nokogiri::XML::Builder.with( xml.doc.root ) do |dot|
    dot.vlans {
      return dot
    }
  end      
end
_xml_rm_ac_untagged_vlan( xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 424
def _xml_rm_ac_untagged_vlan( xml )
  if @under_vlans.empty?
    xml.vlan 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/vlan_l2ng.rb, line 433
def _xml_rm_these_vlans( xml, vlans )
  if @under_vlans.empty?
    xml.vlan( 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.vlan( Netconf::JunosConfig::DELETE ) 
    end
    @under_vlans = []        
  end
end
_xml_rm_under_vlans( xml, vlans ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 410
def _xml_rm_under_vlans( xml, vlans )
  if vlans.any?
    at_vlans = _xml_edit_under_vlans( xml )
    vlans.each do |vlan_name|
      Nokogiri::XML::Builder.with( at_vlans.parent ) do |this|
        this.vlan {
          this.name vlan_name
          this.interface( Netconf::JunosConfig::DELETE ) { this.name @name }
        }
      end
    end
  end    
end
build_catalog() click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 345
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/vlan_l2ng.rb, line 335
def build_list
  begin
    got = @ndev.rpc.get_ethernet_switching_interface_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
upd_tagged_vlans( xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 179
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
    xml.vlan {
      del.each { |v| xml.members v, Netconf::JunosConfig::DELETE }
      add.each { |v| xml.members v }
    }  
  end
  
  return true    
end
upd_untagged_vlan( xml ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 218
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 the ethernet-switching stanza we will need to 'up-out' when making changes to the unit information, like description

# File lib/junos-ez/l2_ports/vlan_l2ng.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/vlan_l2ng.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(:'ethernet-switching') {
      return xml
    }
  }
end
xml_at_top() click to toggle source

XML top placement


# File lib/junos-ez/l2_ports/vlan_l2ng.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/vlan_l2ng.rb, line 127
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 ethernet-switching stanza

# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 144
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/vlan_l2ng.rb, line 174
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/vlan_l2ng.rb, line 213
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/vlan_l2ng.rb, line 155
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/vlan_l2ng.rb, line 34
def xml_get_has_xml( xml )              
  # second unit contains the family/ethernet-switching 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/vlan_l2ng.rb, line 102
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
  
  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/vlan_l2ng.rb, line 49
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/ethernet-switching')        
  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('vlan/members')){ |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('vlan/members').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
  
  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 ) 
  }
end

Private Instance Methods

_get_eth_port_vlans_h( ifs_name ) click to toggle source
# File lib/junos-ez/l2_ports/vlan_l2ng.rb, line 373
def _get_eth_port_vlans_h( ifs_name )
  
  got = @ndev.rpc.get_ethernet_switching_interface_information(:interface_name => 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('l2iff-interface-vlan-name').text.strip      
    tgdy = vlan.xpath('l2iff-interface-vlan-member-tagness').text.strip
    if tgdy == 'untagged'
      ret_h[:untagged] = vlan_name
    else
      ret_h[:tagged] << vlan_name
    end
  end
  ret_h
end