Class: Junos::Ez::L2ports::Provider::VLAN

Inherits:
Junos::Ez::L2ports::Provider show all
Defined in:
lib/junos-ez/l2_ports/vlan.rb,
lib/junos-ez/l2_ports/vlan.rb,
lib/junos-ez/l2_ports/vlan.rb,
lib/junos-ez/l2_ports/vlan.rb,
lib/junos-ez/l2_ports/vlan.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?

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




266
267
268
# File 'lib/junos-ez/l2_ports/vlan.rb', line 266

def self.ac_ac_nountg( this, xml )
  this._xml_rm_ac_untagged_vlan( xml )
end

.ac_ac_untg(this, xml) ⇒ Object


transition where port WILL-HAVE untagged-vlan




289
290
291
292
293
294
# File 'lib/junos-ez/l2_ports/vlan.rb', line 289

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) ⇒ Object



270
271
272
273
274
# File 'lib/junos-ez/l2_ports/vlan.rb', line 270

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) ⇒ Object



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

def self.ac_tr_untg( this, xml )      
  # move untagged vlan to native-vlan-id ...    
  was_untg_vlan = this.has[:untagged_vlan]
  xml.send :'native-vlan-id', 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



255
256
257
258
259
# File 'lib/junos-ez/l2_ports/vlan.rb', line 255

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



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/junos-ez/l2_ports/vlan.rb', line 225

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



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

def self.tr_ac_nountg( this, xml )
  xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE
  this._xml_rm_these_vlans( xml, this.has[:tagged_vlans ] ) if this.has[:tagged_vlans]    
end

.tr_ac_untg(this, xml) ⇒ Object



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

def self.tr_ac_untg( this, xml )    
  xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE 
  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) ⇒ Object



281
282
283
# File 'lib/junos-ez/l2_ports/vlan.rb', line 281

def self.tr_tr_nountg( this, xml )
  xml.send :'native-vlan-id', Netconf::JunosConfig::DELETE              
end

.tr_tr_untg(this, xml) ⇒ Object



309
310
311
# File 'lib/junos-ez/l2_ports/vlan.rb', line 309

def self.tr_tr_untg( this, xml )
  xml.send :'native-vlan-id', this.should[:untagged_vlan]              
end

Instance Method Details

#_xml_edit_under_vlans(xml) ⇒ Object



388
389
390
391
392
393
394
# File 'lib/junos-ez/l2_ports/vlan.rb', line 388

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) ⇒ Object



408
409
410
411
412
413
414
415
# File 'lib/junos-ez/l2_ports/vlan.rb', line 408

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) ⇒ Object



417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/junos-ez/l2_ports/vlan.rb', line 417

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) ⇒ Object



396
397
398
399
400
401
402
403
404
405
406
# File 'lib/junos-ez/l2_ports/vlan.rb', line 396

def _xml_rm_under_vlans( xml, vlans )
  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

#build_catalogObject



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/junos-ez/l2_ports/vlan.rb', line 332

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

#build_listObject



320
321
322
323
324
325
326
327
328
329
330
# File 'lib/junos-ez/l2_ports/vlan.rb', line 320

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

#upd_tagged_vlans(xml) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/junos-ez/l2_ports/vlan.rb', line 164

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) ⇒ Object



203
204
205
# File 'lib/junos-ez/l2_ports/vlan.rb', line 203

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 the ethernet-switching 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
# File 'lib/junos-ez/l2_ports/vlan.rb', line 19

def xml_at_element_top( xml, name )
  xml.interface {
    xml.name name
    xml.unit { 
      xml.name '0'
      return xml
    }
  }    
end

#xml_at_here(xml) ⇒ Object


XML property writers




104
105
106
107
108
109
110
# File 'lib/junos-ez/l2_ports/vlan.rb', line 104

def xml_at_here( xml )
  xml.family {
    xml.send(:'ethernet-switching') {
      return xml
    }
  }
end

#xml_at_topObject


XML top placement




7
8
9
10
11
12
13
# File 'lib/junos-ez/l2_ports/vlan.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



112
113
114
115
116
117
118
119
120
# File 'lib/junos-ez/l2_ports/vlan.rb', line 112

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 ethernet-switching stanza



129
130
131
132
133
134
# File 'lib/junos-ez/l2_ports/vlan.rb', line 129

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




159
160
161
162
# File 'lib/junos-ez/l2_ports/vlan.rb', line 159

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

#xml_change_untagged_vlan(xml) ⇒ Object


:untagged_vlan




198
199
200
201
# File 'lib/junos-ez/l2_ports/vlan.rb', line 198

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

#xml_change_vlan_tagging(xml) ⇒ Object


:vlan_tagging




140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/junos-ez/l2_ports/vlan.rb', line 140

def xml_change_vlan_tagging( xml )    
  port_mode = should_trunk? ? 'trunk' : 'access'
  xml.send(:'port-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




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

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) ⇒ Object

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



93
94
95
96
97
98
# File 'lib/junos-ez/l2_ports/vlan.rb', line 93

def xml_on_delete( xml )
  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



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
# File 'lib/junos-ez/l2_ports/vlan.rb', line 48

def xml_read_parser( as_xml, as_hash )    
  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('port-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    
  
  xml_when_item(f_eth.xpath('native-vlan-id')){|i| as_hash[:untagged_vlan] = i.text.chomp }
  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
  
end