From eb082e22b8431ff9e2b6ac3c776a1f83e7150453 Mon Sep 17 00:00:00 2001
From: Peter Sprygada <sprygada@gmail.com>
Date: Wed, 26 Jun 2013 22:59:23 -0400
Subject: [PATCH] initial arista module import

---
 library/network/arista_interface   | 253 +++++++++++++++++++++++
 library/network/arista_l2interface | 317 +++++++++++++++++++++++++++++
 library/network/arista_lag         | 311 ++++++++++++++++++++++++++++
 library/network/arista_vlan        | 298 +++++++++++++++++++++++++++
 4 files changed, 1179 insertions(+)
 create mode 100644 library/network/arista_interface
 create mode 100644 library/network/arista_l2interface
 create mode 100644 library/network/arista_lag
 create mode 100644 library/network/arista_vlan

diff --git a/library/network/arista_interface b/library/network/arista_interface
new file mode 100644
index 00000000000..77d0a71b085
--- /dev/null
+++ b/library/network/arista_interface
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#    
+# Copyright (C) 2013, Arista Networks <netdevops@aristanetworks.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+DOCUMENTATION = '''
+---
+module: arista_interface
+author: Peter Sprygada
+short_description: Manage physical Ethernet interfaces
+requirements:
+    - Arista EOS 4.10
+    - Netdev extension for EOS
+description:
+    - Manage physical Ethernet interface resources on Arista EOS network devices
+options:
+    interface_id:
+        description:
+            - the full name of the interface
+        required: true
+        default: null
+        aliases: []
+    logging:
+        description:
+            - enables or disables the syslog facility for this module
+        required: false
+        default: false
+        choices: [ 'true', 'false', 'yes', 'no' ]
+        aliases: []
+    admin:
+        description:
+            - controls the operational state of the interface
+        required: false
+        default: null
+        choices: [ 'up', 'down' ]
+        aliases: []
+    description:
+        description:
+            - a single line text string describing the interface
+        required: false
+        default: null
+        aliases: []
+    mtu:
+        description:
+            - configureds the maximum transmission unit for the interface
+        required: false
+        default: 1500
+        aliases: []
+    speed:
+        description:
+            - sets the interface speed setting
+        required: false
+        default: 'auto'
+        choices: [ 'auto', '100m', '1g', '10g' ]
+        aliases: []
+    duplex:
+        description:
+            - sets the interface duplex setting
+        required: false
+        default: 'auto'
+        choices: [ 'auto', 'half', 'full' ]
+        aliases: []
+examples:
+    - code: 'arista_interface: interface_id=Ethernet1 admin="up"'
+      description: "Administratively enable the Ethernet1 interface"
+    - code: 'arista_interface: itnerface_id=Ethernet4 state="default"'
+      descripton: "Default interface Ethernet4"
+'''
+import syslog
+import json
+
+class AristaInterface(object):
+    """ This is the base class for managing physcial Ethernet interface 
+        resources in EOS network devices.  This class acts as a wrapper around 
+        the netdev extension in EOS.  You must have the netdev extension 
+        installed in order for this module to work properly.   
+        
+        The following commands are implemented in this module:
+            * netdev interface list
+            * netdev interface show
+            * netdev interface edit
+            * netdev interface delete
+            
+        This module only allows for the management of physical Ethernet 
+        interfaces.  
+    """
+    
+    attributes = ['interface_id', 'admin', 'description', 'mtu', 'speed', 'duplex']
+    
+    def __init__(self, module):
+        self.module         = module
+        self.interface_id   = module.params['interface_id']
+        self.admin          = module.params['admin']
+        self.description    = module.params['description']
+        self.mtu            = module.params['mtu']
+        self.speed          = module.params['speed']
+        self.duplex         = module.params['duplex']
+        self.logging        = module.params['logging']
+        
+    @property
+    def changed(self):
+        """ The changed property provides a boolean response if the currently
+            loaded resouces has changed from the resource running in EOS.
+            
+            Returns True if the object is not in sync 
+            Returns False if the object is in sync.
+        """
+        return len(self.updates()) > 0
+        
+    def log(self, entry):
+        """ This method is responsible for sending log messages to the local
+            syslog.
+        """
+        if self.logging:
+            syslog.openlog('ansible-%s' % os.path.basename(__file__))
+            syslog.syslog(syslog.LOG_NOTICE, entry)
+                    
+    def run_command(self, cmd):
+        """ Calls the Ansible module run_command method. This method will 
+            directly return the results of the run_command method
+        """        
+        self.log(cmd)
+        return self.module.run_command(cmd.split())
+        
+    def get(self):
+        """ This method will return a dictionary with the attributes of the
+            physical ethernet interface resource specified in interface_id.  
+            The physcial ethernet interface resource has the following 
+            stucture:
+            
+              {
+                "interface_id": <interface_id>,
+                "description": <description>,
+                "admin": [up | down],
+                "mtu": <mtu>,
+                "speed": [auto | 100m | 1g | 10g]
+                "duplex": [auto | half | full]
+              }
+            
+            If the physical ethernet interface specified by interface_id does 
+            not exist in the system, this method will return None.
+        """
+        cmd = "netdev interface show %s" % self.interface_id
+        (rc, out, err) = self.run_command(cmd)
+        obj = json.loads(out)
+        if obj.get('status') != 200: 
+            return None
+        return obj['result']
+        
+    def update(self):
+        """ Updates an existing physical ethernet resource in the current 
+            running configuration.   If the physical ethernet resource does 
+            not exist, this method will return an error.
+            
+            This method implements the following commands:
+                * netdev interface edit {interface_id} [attributes]
+            
+            Returns an updated physical ethernet interafce resoure if the 
+            update method was successful
+        """
+        attribs = list()        
+        for attrib in self.updates():
+            attribs.append("--%s" % attrib)
+            attribs.append(str(getattr(self, attrib)))
+        
+        if attribs:
+            cmd = "netdev interface edit %s " % self.interface_id
+            cmd += " ".join(attribs)
+        
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 200:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+            return (rc, out, err)
+ 
+        return (0, None, "No attributes have been modified")
+        
+    def updates(self):
+        """ This method will check the current phy resource in the running
+            configuration and return a list of attribute that are not in sync
+            with the current resource from the running configuration.
+        """
+        obj = self.get()
+        update = lambda a, z: a != z
+
+        updates = list()
+        for attrib in self.attributes:
+            value = getattr(self, attrib)
+            if update(obj[attrib], value) and value is not None:
+                updates.append(attrib)
+
+        self.log("updates: %s" % updates)
+        return updates
+        
+        
+
+def main():
+    module = AnsibleModule(
+        argument_spec = dict(
+            interface_id=dict(default=None, type='str'),
+            admin=dict(default=None, choices=['up', 'down'], type='str'),
+            description=dict(default=None, type='str'),
+            mtu=dict(default=None, type='int'),
+            speed=dict(default=None, choices=['auto', '100m', '1g', '10g']),
+            duplex=dict(default=None, choices=['auto', 'half', 'full']),
+            logging=dict(default=False, choices=BOOLEANS)
+        ),
+        supports_check_mode = True
+    )
+    
+    obj = AristaInterface(module)
+    
+    rc = None
+    result = dict()
+    
+    if module.check_mode:
+        module.exit_json(changed=obj.changed)
+    
+    else:
+        if obj.changed:
+            (rc, out, err) = obj.update()
+            result['results'] = out
+            if rc is not None and rc != 0:
+                module.fail_json(msg=err, rc=rc)
+
+    if rc is None:
+        result['changed'] = False
+    else:
+        result['changed'] = True
+            
+    module.exit_json(**result)
+    
+
+# include magic from lib/ansible/module_common.py
+#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
+main()
diff --git a/library/network/arista_l2interface b/library/network/arista_l2interface
new file mode 100644
index 00000000000..64ce8b0d2ac
--- /dev/null
+++ b/library/network/arista_l2interface
@@ -0,0 +1,317 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#    
+# Copyright (C) 2013, Arista Networks <netdevops@aristanetworks.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+DOCUMENTATION = '''
+---
+module: arista_l2interface
+author: Peter Sprygada
+short_description: Manage layer 2 interfaces
+requirements:
+    - Arista EOS 4.10
+    - Netdev extension for EOS
+description:
+    - Manage layer 2 interface resources on Arista EOS network devices
+options:
+    interface_id:
+        description:
+            - the full name of the interface
+        required: true
+        default: null
+        aliases: []
+    state:
+        description:
+            - describe the desired state of the interface related to the config
+        required: false
+        default: 'present'
+        choices: [ 'present', 'absent' ]
+        aliases: []
+    logging:
+        description:
+            - enables or disables the syslog facility for this module
+        required: false
+        default: false
+        choices: [ 'true', 'false', 'yes', 'no' ]
+        aliases: []
+    vlan_tagging:
+        description:
+            - specifies whether or not vlan tagging should be enabled for 
+              this interface
+        required: false
+        default: true
+        choices: [ 'enable', 'disable' ]
+        aliases: []
+    tagged_vlans: 
+        description:
+            - specifies the list of vlans that should be allowed to transit
+              this interface
+        required: false
+        default: null
+        aliases: []
+    untagged_vlan:
+        description:
+            - specifies the vlan that untagged traffic should be placed in for
+              transit across a vlan tagged link
+        required: false
+        default: 'default'
+        aliases: []
+examples:
+    - code: 'arista_l2interface: interface_id=Ethernet1 vlan_tagging="enable"'
+      description: "Enable vlan tagging for interface Ethernet1"
+    - code: 'arista_l2interface: interface_id=Ethernet4 tagged_vlans=Blue,Red'
+      descripton: "Specifies vlans Blue & Red should be allowed across this interface"
+'''
+import syslog
+import json
+
+class AristaL2Interface(object):
+    """ This is the base class  managing layer 2 interfaces (switchport) 
+        resources in Arista EOS network devices.  This class provides an 
+        implementation for creating, updating and deleting layer 2 interfaces.
+        
+        Note: The netdev extension for EOS must be installed in order of this
+        module to work properly.
+        
+        The following commands are implemented in this module:
+            * netdev l2interface list
+            * netdev l2interface show
+            * netdev l2interface edit
+            * netdev l2interface delete
+
+    """
+    
+    attributes= ['vlan_tagging', 'tagged_vlans', 'untagged_vlan']
+    
+    def __init__(self, module):
+        self.module         = module
+        self.interface_id   = module.params['interface_id']
+        self.state          = module.params['state']
+        self.vlan_tagging   = module.params['vlan_tagging']
+        self.tagged_vlans   = module.params['tagged_vlans']
+        self.untagged_vlan  = module.params['untagged_vlan']
+        self.logging        = module.params['logging']
+
+    @property
+    def changed(self):
+        """ The changed property provides a boolean response if the currently
+            loaded resouces has changed from the resource running in EOS.
+            
+            Returns True if the object is not in sync 
+            Returns False if the object is in sync.
+        """
+        return len(self.updates()) > 0
+
+    def log(self, entry):
+        """ This method is responsible for sending log messages to the local
+            syslog.
+        """
+        if self.logging:
+            syslog.openlog('ansible-%s' % os.path.basename(__file__))
+            syslog.syslog(syslog.LOG_NOTICE, entry)
+
+    def run_command(self, cmd):
+        """ Calls the Ansible module run_command method. This method will 
+            directly return the results of the run_command method
+        """        
+        self.log("Command: %s" % cmd)
+        return self.module.run_command(cmd.split())
+
+
+    def get(self):
+        """ This method will return a dictionary with the attributes of the
+            layer 2 interface resource specified in interface_id.  The layer
+            2 interface resource has the following stucture:
+            
+              {
+                "interface_id": <interface_id>,
+                "vlan_tagging": [enable* | disable],
+                "tagged_vlans": <array of vlan names>,
+                "untagged_vlan": <vlan name>
+              }
+            
+            If the layer 2 interface specified by interface_id does not
+            exist in the system, this method will return None.
+        """
+        cmd = "netdev l2interface show %s" % self.interface_id
+        (rc, out, err) = self.run_command(cmd)
+        obj = json.loads(out)
+        if obj.get('status') != 200: 
+            return None
+        return obj['result']
+
+    def create(self):
+        """ Creates a layer 2 interface resource in the current running 
+            configuration.  If the layer 2 interface already exists, the 
+            function will return successfully.
+            
+            This function implements the following commands:
+                * netdev l2interface create {interface_id} [attributes]
+            
+            Returns the layer 2 interface resource if the create method was 
+            successful
+            Returns an error message if there as a problem creating the layer
+            2 interface
+        """
+        attribs = []
+        for attrib in self.attributes:
+            if getattr(self, attrib):
+                attribs.append("--%s" % attrib)
+                attribs.append(getattr(self, attrib))
+
+            cmd = "netdev l2interface create %s " % self.interface_id
+            cmd += " ".join(attribs)
+
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 201:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+        return (rc, out, err)
+
+    def update(self):
+        """ Updates an existing VLAN resource in the current running 
+            configuration.   If the VLAN resource does not exist, this method
+            will return an error.
+            
+            This method implements the following commands:
+                * netdev l2interface edit {interface_id} [attributes]
+            
+            Returns an updated layer 2 interafce resoure if the update method 
+            was successful
+        """
+        attribs = list()        
+        for attrib in self.updates():
+            attribs.append("--%s" % attrib)
+            attribs.append(getattr(self, attrib))
+
+            cmd = "netdev l2interface edit %s " % self.interface_id
+            cmd += " ".join(attribs)
+
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 200:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+            return (rc, out, err)
+  
+        return (0, None, "No attributes have been modified")
+
+    def delete(self):
+        """ Deletes an existing layer 2 interface resource from the current 
+            running configuration.  A nonexistent layer 2 interface will 
+            return successful for this operation.
+            
+            This method implements the following commands:
+                * netdev l2interface delete {interface_id}
+            
+            Returns nothing if the delete was successful
+            Returns error message if there was a problem deleting the resource 
+        """
+        cmd = "netdev l2interface delete %s" % self.interface_id
+        (rc, out, err) = self.run_command(cmd)
+        resp = json.loads(out)
+        if resp.get('status') != 200: 
+            rc = resp['status']
+            err = resp['message']
+            out = None
+        return (rc, out, err)
+
+    def updates(self):
+        """ This method will check the current layer 2 interface resource in 
+            the running configuration and return a list of attributes that are
+            not in sync with the current resource.
+        """
+        obj = self.get()
+        update = lambda a, z: a != z
+
+        updates = list()
+        for attrib in self.attributes:
+            value = getattr(self, attrib)
+            if update(obj[attrib], value) and value is not None:
+                updates.append(attrib)
+        self.log("Updates: %s" % updates)
+        return updates
+
+    def exists(self):
+        """ Returns True if the current layer 2 interface resource exists and 
+            returns False if it does not.   This method only checks for the 
+            existence of the interface as specified in interface_id.
+        """
+        (rc, out, err) = self.run_command("netdev l2interface list")
+        collection = json.loads(out)
+        return collection.get('result').has_key(self.interface_id)
+        
+
+def main():
+    module = AnsibleModule(
+        argument_spec = dict(
+            interface_id=dict(default=None, type='str'),
+            state=dict(default='present', choices=['present', 'absent'], type='str'),
+            vlan_tagging=dict(default=None, choices=['enable', 'disable']),
+            tagged_vlans=dict(default=None, type='str'),
+            untagged_vlan=dict(default=None, type='str'),
+            logging=dict(default=False, choices=BOOLEANS)
+        ),
+        supports_check_mode = True
+    )
+    
+    obj = AristaL2Interface(module)
+    
+    rc = None
+    result = dict()
+
+    if obj.state == 'absent':
+        if obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.delete()
+            if rc !=0: 
+                module.fail_json(msg=err, rc=rc)                
+
+    elif obj.state == 'present':
+        if not obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.create()
+            result['results'] = out
+        else:
+            if obj.changed:
+                if module.check_mode: 
+                    module.exit_json(changed=obj.changed)
+                (rc, out, err) = obj.update()
+                result['results'] = out
+
+        if rc is not None and rc != 0:
+            module.fail_json(msg=err, rc=rc)
+
+    if rc is None:
+        result['changed'] = False
+    else:
+        result['changed'] = True
+
+    module.exit_json(**result)
+
+
+# include magic from lib/ansible/module_common.py
+#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
+main()
diff --git a/library/network/arista_lag b/library/network/arista_lag
new file mode 100644
index 00000000000..2523215f617
--- /dev/null
+++ b/library/network/arista_lag
@@ -0,0 +1,311 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#    
+# Copyright (C) 2013, Arista Networks <netdevops@aristanetworks.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+DOCUMENTATION = '''
+---
+module: arista_lag
+author: Peter Sprygada
+short_description: Manage port channel (lag) interfaces
+requirements:
+    - Arista EOS 4.10
+    - Netdev extension for EOS
+description:
+    - Manage port channel (lag) interfaceresources on Arista EOS network devices
+options:
+    interface_id:
+        description:
+            - the full name of the interface
+        required: true
+        default: null
+        aliases: []
+    state:
+        description:
+            - describe the desired state of the interface related to the config
+        required: false
+        default: 'present'
+        choices: [ 'present', 'absent' ]
+        aliases: []
+    logging:
+        description:
+            - enables or disables the syslog facility for this module
+        required: false
+        default: false
+        choices: [ 'true', 'false', 'yes', 'no' ]
+        aliases: []
+    links:
+        description:
+            - array of physical interface links to include in this lag
+        required: false
+        default: null
+        aliases: []
+    minimum_links:
+        description:
+            - the minimum number of physical interaces that must be operationally up to consider the lag operationally up
+        required: false
+        default: null
+        aliases: []
+    lacp:
+        description:
+            - enables the use of the LACP protocol for managing link bundles
+        required: false
+        default: 'active',
+        choices: [ 'active', 'passive', 'off' ]
+        aliases: []
+examples:
+    - code: 'arista_lag: interface_id=Port-Channel10 links=Ethernet1,Ethernet2'
+      description: "Configure Port-Channel 10 with physical interfaces Ethernet1 and Ethernet2 as members"
+'''
+import syslog
+import json
+
+class AristaLag(object):
+    """ This is the base class managing port-channel (lag) interfaces 
+        resources in Arista EOS network devices.  This class provides an 
+        implementation for creating, updating and deleting port-channel 
+        interfaces.
+        
+        Note: The netdev extension for EOS must be installed in order of this
+        module to work properly.
+        
+        The following commands are implemented in this module:
+            * netdev lag list
+            * netdev lag show
+            * netdev lag edit
+            * netdev lag delete
+        
+    """
+    
+    attributes = ['links', 'minimum_links', 'lacp']
+    
+    def __init__(self, module):
+        self.module         = module
+        self.interface_id   = module.params['interface_id']
+        self.state          = module.params['state']
+        self.links          = module.params['links']
+        self.minimum_links  = module.params['minimum_links']
+        self.lacp           = module.params['lacp']
+        self.logging        = module.params['logging']
+                    
+    @property
+    def changed(self):
+        """ The changed property provides a boolean response if the currently
+            loaded resouces has changed from the resource running in EOS.
+            
+            Returns True if the object is not in sync 
+            Returns False if the object is in sync.
+        """
+        return len(self.updates()) > 0
+
+    def log(self, entry):
+        """ This method is responsible for sending log messages to the local
+            syslog.
+        """
+        if self.logging:
+            syslog.openlog('ansible-%s' % os.path.basename(__file__))
+            syslog.syslog(syslog.LOG_NOTICE, entry)
+
+    def run_command(self, cmd):
+        """ Calls the Ansible module run_command method. This method will 
+            directly return the results of the run_command method
+        """      
+        self.log("Command: %s" % cmd)
+        return self.module.run_command(cmd.split())
+
+        
+    def get(self):
+        """ This method will return a dictionary with the attributes of the
+            lag interface resource specified in interface_id.  The lag
+            interface resource has the following stucture:
+            
+              {
+                "interface_id": <interface_id>,
+                "links": <array of member interfaces>,
+                "minimum_links": <minimum_links>,
+                "lacp": [active* | passive | off]
+              }
+            
+            If the lag interface specified by interface_id does not
+            exist in the system, this method will return None.
+        """
+        cmd = "netdev lag show %s" % self.interface_id
+        (rc, out, err) = self.run_command(cmd)
+        obj = json.loads(out)
+        if obj.get('status') != 200: 
+            return None
+        return obj['result']
+        
+    def create(self):
+        """ Creates a lag interface resource in the current running 
+            configuration.  If the lag interface already exists, the 
+            function will return successfully.  
+            
+            This function implements the following commands:
+                * netdev lag create {interface_id} [attributes]
+            
+            Returns the lag interface resource if the create method was 
+            successful
+            Returns an error message if there as a problem creating the lag
+            interface
+        """
+        attribs = []
+        for attrib in self.attributes:
+            if getattr(self, attrib):
+                attribs.append("--%s" % attrib)
+                attribs.append(getattr(self, attrib))
+        
+            cmd = "netdev lag create %s " % self.interface_id
+            cmd += " ".join(attribs)
+            
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 201:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = self.get()
+        return (rc, out, err)
+        
+    def update(self):    
+        """ Updates an existing lag resource in the current running 
+            configuration.   If the lag resource does not exist, this method
+            will return an error.
+            
+            This method implements the following commands:
+                * netdev lag edit {interface_id} [attributes]
+            
+            Returns an updated lag interafce resoure if the update method 
+            was successful
+        """
+        attribs = list()        
+        for attrib in self.updates():
+            attribs.append("--%s" % attrib)
+            attribs.append(getattr(self, attrib))
+        
+            cmd = "netdev lag edit %s " % self.interface_id
+            cmd += " ".join(attribs)
+        
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 200:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+            return (rc, out, err)
+ 
+        return (2, None, "No attributes have been modified")
+        
+    def delete(self):
+        """ Deletes an existing lag interface resource from the current 
+            running configuration.  A nonexistent lag interface will 
+            return successful for this operation.
+            
+            This method implements the following commands:
+                * netdev lag delete {interface_id}
+            
+            Returns nothing if the delete was successful
+            Returns error message if there was a problem deleting the resource 
+        """
+        cmd = "netdev lag delete %s" % self.interface_id
+        (rc, out, err) = self.run_command(cmd)
+        resp = json.loads(out)
+        if resp.get('status') != 200: 
+            rc = resp['status']
+            err = resp['message']
+            out = None
+        return (rc, out, err)
+        
+    def updates(self):
+        """ This method will check the current lag interface resource in the
+            running configuration and return a list of attributes that are
+            not in sync with the current resource.
+        """
+        obj = self.get()
+        update = lambda a, z: a != z
+
+        updates = list()
+        for attrib in self.attributes:
+            if update(obj[attrib], getattr(self, attrib)):
+                updates.append(attrib)
+
+        return updates
+
+    def exists(self):
+        """ Returns True if the current lag interface resource exists and 
+            returns False if it does not.   This method only checks for the 
+            existence of the interface as specified in interface_id.
+        """
+        (rc, out, err) = self.run_command("netdev lag list")
+        collection = json.loads(out)
+        return collection.get('result').has_key(self.interface_id)
+        
+
+def main():
+    module = AnsibleModule(
+        argument_spec = dict(
+            interface_id=dict(default=None, type='str'),
+            state=dict(default='present', choices=['present', 'absent'], type='str'),
+            links=dict(default=None, type='str'),
+            lacp=dict(default=None, choices=['active', 'passive', 'off'], type='str'),
+            minimum_links=dict(default=None, type='int'),
+            logging=dict(default=False, choices=BOOLEANS)
+        ),
+        supports_check_mode = True
+    )
+    
+    obj = AristaLag(module)
+    
+    rc = None
+    result = dict()
+
+    if obj.state == 'absent':
+        if obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.delete()
+            if rc !=0: 
+                module.fail_json(msg=err, rc=rc)                
+
+    elif obj.state == 'present':
+        if not obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.create()
+            result['results'] = out
+        else:
+            if module.check_mode: 
+                module.exit_json(changed=obj.changed)
+            (rc, out, err) = obj.update()
+            result['results'] = out
+
+        if rc is not None and rc != 0:
+            module.fail_json(msg=err, rc=rc)
+
+    if rc is None:
+        result['changed'] = False
+    else:
+        result['changed'] = True
+
+    module.exit_json(**result)
+
+
+# include magic from lib/ansible/module_common.py
+#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
+main()
diff --git a/library/network/arista_vlan b/library/network/arista_vlan
new file mode 100644
index 00000000000..9c64cded024
--- /dev/null
+++ b/library/network/arista_vlan
@@ -0,0 +1,298 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#    
+# Copyright (C) 2013, Arista Networks <netdevops@aristanetworks.com>
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+DOCUMENTATION = '''
+---
+module: arista_vlan
+author: Peter Sprygada
+short_description: Manage VLAN resources
+requirements:
+    - Arista EOS 4.10
+    - Netdev extension for EOS
+description:
+    - Manage VLAN resources on Arista EOS network devices
+options:
+    vlan_id:
+        description:
+            - the vlan id 
+        required: true
+        default: null
+        aliases: []
+    state:
+        description:
+            - describe the desired state of the vlan related to the config
+        required: false
+        default: 'present'
+        choices: [ 'present', 'absent' ]
+        aliases: []
+    logging:
+        description:
+            - enables or disables the syslog facility for this module
+        required: false
+        default: false
+        choices: [ 'true', 'false', 'yes', 'no' ]
+        aliases: []
+    name:
+        description:
+            - a descriptive name for the vlan
+        required: false
+        default: null
+        aliases: []
+examples:
+    - code: 'arista_vlan: vlan_id=100 name="Blue"'
+      description: "Creates vlan 100 in the running config"
+    - code: 'arista_vlan: vlan_id=200 state="absent"'
+      descripton: "Ensures vlan 200 is not in the config"
+notes:
+    - Requires EOS 4.10 or later 
+    - The Netdev extension for EOS must be installed and active in the 
+      available extensions (show extensions from the EOS CLI)
+'''
+import syslog
+import json
+
+class AristaVlan(object):
+    """ This is the base class for managing VLAN resources in EOS network 
+        devices.   This class provides basic CRUD functions for VLAN 
+        resources.  This class acts as a wrapper around the netdev extension
+        in EOS.  You must have the netdev extension installed in order for
+        this module to work properly.   
+        
+        The following commands are implemented in this module:
+            * netdev vlan create
+            * netdev vlan list
+            * netdev vlan show
+            * netdev vlan edit
+            * netdev vlan delete
+    """
+    
+    attributes = ['name']
+
+    def __init__(self, module):
+        self.module     = module
+        self.vlan_id    = module.params['vlan_id']
+        self.name       = module.params['name']
+        self.state      = module.params['state']
+        self.logging    = module.boolean(module.params['logging'])
+        
+        
+    @property
+    def changed(self):
+        """ The changed property provides a boolean response if the currently
+            loaded resouces has changed from the resource running in EOS.
+            
+            Returns True if the object is not in sync 
+            Returns False if the object is in sync.
+        """
+        return len(self.updates()) > 0
+        
+    def log(self, entry):
+        """ This method is responsible for sending log messages to the local
+            syslog.
+        """
+        if self.logging:
+            syslog.openlog('ansible-%s' % os.path.basename(__file__))
+            syslog.syslog(syslog.LOG_INFO, entry)
+    
+    def run_command(self, cmd):
+        """ Calls the Ansible module run_command method.  This method will
+            also send a message to syslog with the command name
+        """        
+        self.log("Command: %s" % cmd)
+        return self.module.run_command(cmd.split())
+        
+        
+    def delete(self):
+        """ Deletes an existing VLAN resource from the current running 
+            configuration.  A nonexistent VLAN will return successful for this
+            operation.
+            
+            This method implements the following commands:
+                * netdev vlan delete {vlan_id}
+            
+            Returns nothing if the delete was successful
+            Returns error message if there was a problem deleting the vlan 
+        """
+        cmd = "netdev vlan delete %s" % self.vlan_id
+        (rc, out, err) = self.run_command(cmd)
+        resp = json.loads(out)
+        if resp.get('status') != 200: 
+            rc = resp['status']
+            err = resp['message']
+            out = None
+        return (rc, out, err)
+        
+    def create(self):
+        """ Creates a VLAN resource in the current running configuration.  If 
+            the VLAN already exists, the function will return successfully.
+            
+            This function implements the following commands:
+                * netdev vlan create {vlan_id} [--name <name>]
+            
+            Returns the VLAN resource if the create function was successful
+            Returns an error message if there as a problem creating the vlan
+        """
+        attribs = []
+        for attrib in self.attributes:
+            if getattr(self, attrib):
+                attribs.append("--%s" % attrib)
+                attribs.append(getattr(self, attrib))
+        
+            cmd = "netdev vlan create %s " % self.vlan_id
+            cmd += " ".join(attribs)
+            
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 201:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+        return (rc, out, err)
+        
+    def update(self):
+        """ Updates an existing VLAN resource in the current running 
+            configuration.   If the VLAN resource does not exist, this method
+            will return an error.
+            
+            This method implements the following commands:
+                * netdev vlan edit {vlan_id} [--name <name>]
+            
+            Returns an updated VLAN resoure if the create method was successful
+        """
+        attribs = list()        
+        for attrib in self.updates():
+            attribs.append("--%s" % attrib)
+            attribs.append(getattr(self, attrib))
+        
+        if attribs:
+            cmd = "netdev vlan edit %s " % self.vlan_id
+            cmd += " ".join(attribs)
+        
+            (rc, out, err) = self.run_command(cmd)
+            resp = json.loads(out)
+            if resp.get('status') != 200:
+                rc = int(resp['status'])
+                err = resp['message']
+                out = None
+            else:
+                out = resp['result']
+            return (rc, out, err)
+          
+        return (0, None, "No attributes have been modified")
+    
+    def updates(self):
+        """ This method will check the current VLAN resource in the running
+            configuration and return a list of attributes that are not in sync
+            with the current resource from the running configuration.
+        """
+        obj = self.get()
+        update = lambda a, z: a != z
+        
+        updates = list()
+        for attrib in self.attributes:
+            value = getattr(self, attrib)
+            if update(obj[attrib], update) and value is not None:
+                updates.append(attrib)
+        self.log("updates: %s" % updates)
+        return updates
+        
+    def exists(self):
+        """ Returns True if the current VLAN resource exists and returns False
+            if it does not.   This method only checks for the existence of the
+            VLAN ID.
+        """
+        (rc, out, err) = self.run_command("netdev vlan list")
+        collection = json.loads(out)
+        return collection.get('result').has_key(str(self.vlan_id))
+        
+    def get(self):
+        """ This method will return a dictionary with the attributes of the
+            VLAN resource identified in vlan_id.   The VLAN resource has the 
+            following stucture:
+            
+              {
+                "vlan_id": <vlan_id>,
+                "name": <name>
+              }
+            
+            If the VLAN ID specified by vlan_id does not exist in the system, 
+            this method will return None
+        """
+        cmd = "netdev vlan show %s" % self.vlan_id
+        (rc, out, err) = self.run_command(cmd)
+        obj = json.loads(out)
+        if obj.get('status') != 200: 
+            return None
+        return obj['result']
+        
+        
+
+def main():
+
+    module = AnsibleModule(
+        argument_spec = dict(
+            vlan_id=dict(default=None, required=True, type='int'),
+            name=dict(default=None, type='str'),
+            state=dict(default='present', choices=['present', 'absent']),
+            logging=dict(default=False, choices=BOOLEANS)
+        ),
+        supports_check_mode = True
+    )
+    
+    obj = AristaVlan(module)
+    
+    rc = None
+    result = dict()
+    
+    if obj.state == 'absent':
+        if obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.delete()
+            if rc !=0: 
+                module.fail_json(msg=err, rc=rc)                
+            
+    elif obj.state == 'present':
+        if not obj.exists():
+            if module.check_mode: 
+                module.exit_json(changed=True)
+            (rc, out, err) = obj.create()
+            result['results'] = out
+        else:
+            if obj.changed:
+                if module.check_mode: 
+                    module.exit_json(changed=obj.changed)
+                (rc, out, err) = obj.update()
+                result['results'] = out
+            
+        if rc is not None and rc != 0:
+            module.fail_json(msg=err, rc=rc)
+    
+    if rc is None:
+        result['changed'] = False
+    else:
+        result['changed'] = True
+    
+    module.exit_json(**result)
+    
+
+# include magic from lib/ansible/module_common.py
+#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
+main()