This allows to manage the global device list of vlans.
Currently supports only cisco IOS devices.

This is as easy as:

Vlan {
 device_url => "ssh://user:[email protected]/"
}

vlan {
 "200": description => "R&D";
 "99":  description => "Management";
}

The device_url conforms to the same specs as for the interface
type.

Signed-off-by: Brice Figureau <[email protected]>
---
 lib/puppet/provider/vlan/cisco.rb                  |   34 +++++++++++
 lib/puppet/type/vlan.rb                            |   24 ++++++++
 lib/puppet/util/network_device/cisco/device.rb     |   25 +++++++-
 spec/unit/provider/vlan/cisco_spec.rb              |   62 ++++++++++++++++++++
 spec/unit/type/vlan_spec.rb                        |   40 +++++++++++++
 spec/unit/util/network_device/cisco/device_spec.rb |   22 +++++++-
 6 files changed, 204 insertions(+), 3 deletions(-)
 create mode 100644 lib/puppet/provider/vlan/cisco.rb
 create mode 100644 lib/puppet/type/vlan.rb
 create mode 100644 spec/unit/provider/vlan/cisco_spec.rb
 create mode 100644 spec/unit/type/vlan_spec.rb

diff --git a/lib/puppet/provider/vlan/cisco.rb 
b/lib/puppet/provider/vlan/cisco.rb
new file mode 100644
index 0000000..46e172c
--- /dev/null
+++ b/lib/puppet/provider/vlan/cisco.rb
@@ -0,0 +1,34 @@
+require 'puppet/util/network_device/cisco/device'
+require 'puppet/provider/network_device'
+
+Puppet::Type.type(:vlan).provide :cisco, :parent => 
Puppet::Provider::NetworkDevice do
+
+  desc "Cisco switch/router provider for vlans."
+
+  mk_resource_methods
+
+  def self.lookup(url, id)
+    vlans = {}
+    device = Puppet::Util::NetworkDevice::Cisco::Device.new(url)
+    device.command do |d|
+      vlans = d.parse_vlans || {}
+    end
+    vlans[id]
+  end
+
+  def initialize(*args)
+    super
+  end
+
+  # Clear out the cached values.
+  def flush
+    device.command do |device|
+      device.update_vlan(resource[:name], former_properties, properties)
+    end
+    super
+  end
+
+  def device
+    @device ||= 
Puppet::Util::NetworkDevice::Cisco::Device.new(resource[:device_url])
+  end
+end
diff --git a/lib/puppet/type/vlan.rb b/lib/puppet/type/vlan.rb
new file mode 100644
index 0000000..6708ea4
--- /dev/null
+++ b/lib/puppet/type/vlan.rb
@@ -0,0 +1,24 @@
+#
+# Manages a Vlan on a given router or switch
+#
+
+Puppet::Type.newtype(:vlan) do
+    @doc = "This represents a router or switch vlan."
+
+    ensurable
+
+    newparam(:name) do
+      desc "Vlan id. It must be a number"
+      isnamevar
+
+      newvalues(/^\d+/)
+    end
+
+    newproperty(:description) do
+      desc "Vlan name"
+    end
+
+    newparam(:device_url) do
+      desc "Url to connect to a router or switch."
+    end
+end
\ No newline at end of file
diff --git a/lib/puppet/util/network_device/cisco/device.rb 
b/lib/puppet/util/network_device/cisco/device.rb
index 97489bd..1f35099 100644
--- a/lib/puppet/util/network_device/cisco/device.rb
+++ b/lib/puppet/util/network_device/cisco/device.rb
@@ -163,11 +163,11 @@ class Puppet::Util::NetworkDevice::Cisco::Device < 
Puppet::Util::NetworkDevice::
       case l
             # vlan    name    status
       when /^(\d+)\s+(\w+)\s+(\w+)\s+([a-zA-Z0-9,\/. ]+)\s*$/
-        vlan = { :id => $1, :name => $2, :status => $3, :interfaces => [] }
+        vlan = { :name => $1, :description => $2, :status => $3, :interfaces 
=> [] }
         if $4.strip.length > 0
           vlan[:interfaces] = $4.strip.split(/\s*,\s*/).map{ |ifn| 
canonalize_ifname(ifn) }
         end
-        vlans[vlan[:id]] = vlan
+        vlans[vlan[:name]] = vlan
       when /^\s+([a-zA-Z0-9,\/. ]+)\s*$/
         raise "invalid sh vlan summary output" unless vlan
         if $1.strip.length > 0
@@ -179,6 +179,27 @@ class Puppet::Util::NetworkDevice::Cisco::Device < 
Puppet::Util::NetworkDevice::
     vlans
   end
 
+  def update_vlan(id, is = {}, should = {})
+    if should[:ensure] == :absent
+      Puppet.info "Removing #{id} from device vlan"
+      transport.command("conf t")
+      transport.command("no vlan #{id}")
+      transport.command("exit")
+      return
+    end
+
+    # We're creating or updating an entry
+    transport.command("conf t")
+    transport.command("vlan #{id}")
+    [is.keys, should.keys].flatten.uniq.each do |property|
+      Puppet.debug("trying property: #{property}: #{should[property]}")
+      next if property != :description
+      transport.command("name #{should[property]}")
+    end
+    transport.command("exit")
+    transport.command("exit")
+  end
+
   def parse_trunking(interface)
     trunking = {}
     out = transport.command("sh interface #{interface} switchport")
diff --git a/spec/unit/provider/vlan/cisco_spec.rb 
b/spec/unit/provider/vlan/cisco_spec.rb
new file mode 100644
index 0000000..0951367
--- /dev/null
+++ b/spec/unit/provider/vlan/cisco_spec.rb
@@ -0,0 +1,62 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+require 'puppet/provider/vlan/cisco'
+
+provider_class = Puppet::Type.type(:vlan).provider(:cisco)
+
+describe provider_class do
+  before do
+    @resource = stub("resource", :name => "200")
+    @provider = provider_class.new(@resource)
+  end
+
+  it "should have a parent of Puppet::Provider::NetworkDevice" do
+    provider_class.should < Puppet::Provider::NetworkDevice
+  end
+
+  it "should have an instances method" do
+    provider_class.should respond_to(:instances)
+  end
+
+  describe "when looking up instances at prefetch" do
+    before do
+      @device = stub_everything 'device'
+      Puppet::Util::NetworkDevice::Cisco::Device.stubs(:new).returns(@device)
+      @device.stubs(:command).yields(@device)
+    end
+
+    it "should initialize the network device with the given url" do
+      
Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).with(:url).returns(@device)
+      provider_class.lookup(:url, "200")
+    end
+
+    it "should delegate to the device vlans" do
+      @device.expects(:parse_vlans)
+      provider_class.lookup("", "200")
+    end
+
+    it "should return only the given vlan" do
+      @device.expects(:parse_vlans).returns({"200" => { :description => 
"thisone" }, "1" => { :description => "nothisone" }})
+      provider_class.lookup("", "200").should == {:description => "thisone" }
+    end
+
+  end
+
+  describe "when an instance is being flushed" do
+    it "should call the device update_vlan method with its vlan id, current 
attributes, and desired attributes" do
+      @instance = provider_class.new(:ensure => :present, :name => "200", 
:description => "myvlan")
+      @instance.description = "myvlan2"
+      @instance.resource = @resource
+      @resource.stubs(:[]).with(:name).returns("200")
+      device = stub_everything 'device'
+      @instance.stubs(:device).returns(device)
+      device.expects(:command).yields(device)
+      device.expects(:update_vlan).with(@instance.name, {:ensure => :present, 
:name => "200", :description => "myvlan"},
+                                                   {:ensure => :present, :name 
=> "200", :description => "myvlan2"})
+
+      @instance.flush
+    end
+  end
+end
diff --git a/spec/unit/type/vlan_spec.rb b/spec/unit/type/vlan_spec.rb
new file mode 100644
index 0000000..607d711
--- /dev/null
+++ b/spec/unit/type/vlan_spec.rb
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Type.type(:vlan) do
+  it "should have a 'name' parameter'" do
+    Puppet::Type.type(:vlan).new(:name => "200")[:name].should == "200"
+  end
+
+  it "should have a 'device_url' parameter'" do
+    Puppet::Type.type(:vlan).new(:name => "200", :device_url => 
:device)[:device_url].should == :device
+  end
+
+  it "should have an ensure property" do
+    Puppet::Type.type(:vlan).attrtype(:ensure).should == :property
+  end
+
+  it "should have a description property" do
+    Puppet::Type.type(:vlan).attrtype(:description).should == :property
+  end
+
+  describe "when validating attribute values" do
+    before do
+      @provider = stub 'provider', :class => 
Puppet::Type.type(:vlan).defaultprovider, :clear => nil
+      Puppet::Type.type(:vlan).defaultprovider.stubs(:new).returns(@provider)
+    end
+
+    it "should support :present as a value to :ensure" do
+      Puppet::Type.type(:vlan).new(:name => "200", :ensure => :present)
+    end
+
+    it "should support :absent as a value to :ensure" do
+      Puppet::Type.type(:vlan).new(:name => "200", :ensure => :absent)
+    end
+
+    it "should fail if vlan name is not a number" do
+      lambda { Puppet::Type.type(:vlan).new(:name => "notanumber", :ensure => 
:present) }.should raise_error
+    end
+  end
+end
diff --git a/spec/unit/util/network_device/cisco/device_spec.rb 
b/spec/unit/util/network_device/cisco/device_spec.rb
index 9021bbd..31aec92 100644
--- a/spec/unit/util/network_device/cisco/device_spec.rb
+++ b/spec/unit/util/network_device/cisco/device_spec.rb
@@ -150,6 +150,26 @@ eos
     end
   end
 
+  describe "when updating device vlans" do
+    describe "when removing a vlan" do
+      it "should issue the no vlan command" do
+        @transport.expects(:command).with("no vlan 200")
+        @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { 
:ensure=> :absent})
+      end
+    end
+
+    describe "when updating a vlan" do
+      it "should issue the vlan command to enter global vlan modifications" do
+        @transport.expects(:command).with("vlan 200")
+        @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { 
:ensure=> :present, :name => "200"})
+      end
+
+      it "should issue the name command to modify the vlan description" do
+        @transport.expects(:command).with("name myvlan")
+        @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { 
:ensure=> :present, :name => "200", :description => "myvlan"})
+      end
+    end
+  end
 
   describe "when parsing interface" do
 
@@ -228,7 +248,7 @@ VLAN Name                             Status    Ports
 Switch#
 eos
 
-      @cisco.parse_vlans.should == {"100"=>{:status=>"active", 
:interfaces=>["FastEthernet0/1", "FastEthernet0/2"], :name=>"management", 
:id=>"100"}, "1"=>{:status=>"active", :interfaces=>["FastEthernet0/3", 
"FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6", "FastEthernet0/7", 
"FastEthernet0/8", "FastEthernet0/9", "FastEthernet0/10", "FastEthernet0/11", 
"FastEthernet0/12", "FastEthernet0/13", "FastEthernet0/14", "FastEthernet0/15", 
"FastEthernet0/16", "FastEthernet0/17", "FastEthernet0/18", "FastEthernet0/23", 
"FastEthernet0/24"], :name=>"default", :id=>"1"}, "10"=>{:status=>"active", 
:interfaces=>[], :name=>"VLAN0010", :id=>"10"}}
+      @cisco.parse_vlans.should == {"100"=>{:status=>"active", 
:interfaces=>["FastEthernet0/1", "FastEthernet0/2"], 
:description=>"management", :name=>"100"}, "1"=>{:status=>"active", 
:interfaces=>["FastEthernet0/3", "FastEthernet0/4", "FastEthernet0/5", 
"FastEthernet0/6", "FastEthernet0/7", "FastEthernet0/8", "FastEthernet0/9", 
"FastEthernet0/10", "FastEthernet0/11", "FastEthernet0/12", "FastEthernet0/13", 
"FastEthernet0/14", "FastEthernet0/15", "FastEthernet0/16", "FastEthernet0/17", 
"FastEthernet0/18", "FastEthernet0/23", "FastEthernet0/24"], 
:description=>"default", :name=>"1"}, "10"=>{:status=>"active", 
:interfaces=>[], :description=>"VLAN0010", :name=>"10"}}
     end
 
     it "should parse trunk switchport information" do
-- 
1.7.2.1

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to