HomeGuidesAPI Reference
GuidesAPI ReferenceGitHubAirheads Developer CommunityLog In

Using the pyaoscx Package

Using the pyaoscx package

For instructions on how to install the latest version of the pyaoscx package follow the instructions in the Installing pyaoscx v2 guide.

pyaoscx v2.0.0+ Package Structure

pyaoscx
│   README.md
│   CONTRIBUTING.md
│   ...
│
└───pyaoscx
│   │   CONTRIBUTING.md
│   │   DESIGN.md
│   │   acl.py
│   │   acl_entry.py
│   │   ...
│
└───docs
│   │   ...
│
└───workflows
    │  	workflow.py

The pyaoscx package is a directory containing files and subdirectories. Contained directly within the top level pyaoscx directory are informative files such as the readme, licensing information, contribution guidelines, and release notes. Also contained directly within the top level directory are the relevant code-containing directories pyaoscx and workflows.

Using pyaoscx Modules

The pyaoscx subfolder contains the AOS-CX Python modules which uses the object-oriented approach. Instead of having separate modules to perform specific operations, it connects several features together to represent the operational state of the switch. This allows users to keep track of the switch configuration in its objects and internal data structures.

Every single feature is represented in a class, classes may contain and depend on references on additional classes or features. For example, the Interface represents a specific Interface, and it may contain references to other VLANs, VRFs, and depending on the use case, the VSF or VSX configuration. Detailed module documentation can be found on the pyaoscx readthedocs.

Each module contains function definitions which either make a single REST API call or multiple REST API calls. Each function that makes multiple REST API calls is written as such to perform a small logical low-level process (e.g. one call creates a VLAN and the next creates a corresponding VLAN interface). The REST API calls are performed using the Python requests library, which provides functions to make HTTP GET, PUT, POST, and DELETE requests. Information about the requests library can be found here.

All module or feature creation is managed through a flag attribute materialized. This attribute will be True if the module exists within the device, False otherwise. This will be updated whenever a POST or GET call is executed.

Each pyaoscx Module has a list of important attributes besides materialized:

  • session: used to make the HTTP requests, among other things.
  • config_attrs: list of attributes writable to each specific object
  • original_attributes: dictionary with object information and attributes right
    after performing a GET request.
  • modified: Flag to verify if object was modified, in case it was not the PUT
    request is not made.

📘

Design document available

Our design document is available in our Github repository and outlines the structure of the pyaoscx package and how to use it.

Using the pyaoscx Package

After installing the package, you'll be able to run and create your own workflows.

There's an example workflow available in the Github repository that you can pull and use as a template for custom workflows.

First clone the repository:

ubuntu-vm$ git clone https://github.com/aruba/pyaoscx.git

Change into the newly cloned repository and enter the /workflows/ directory to view the workflow.py file:

ubuntu-vm$cd pyaoscx/
ubuntu-vm$cd workflows/
ubuntu-vm$pwd
/home/sandbox/pyaoscx/workflows
ubuntu-vm$ls
__init__.py  workflow.py

In this object oriented approach there are two ways to configure the AOS-CX platform, the open granulated approach and the imperative factory approach. Both methods require a Session object which is used as the primary connection source to the CX switch.

from pyaoscx.session import Session

version = "10.04"
switch_ip = "172.25.0.2"
s = Session(switch_ip, version)
s.open("admin", "admin")

Once you have your Session object you can begin configuring features of the CX switch using Classes and functions. In the example workflow.py file provided in the repository, it shows the two different approaches to configuring features using the pyaoscx package:

from pyaoscx.session import Session
from pyaoscx.pyaoscx_factory import PyaoscxFactory
from pyaoscx.vlan import Vlan
from pyaoscx.interface import Interface

# There are two approaches to workflows, both using the session.
version = "10.04"
switch_ip = "172.25.0.2"
s = Session(switch_ip, version)
s.open("admin", "admin")

# Try block is used so that session closes even on error.
try:
    # APPROACH 1: OPEN GRANULATED APPROACH
    # VLAN
    # Create Vlan object -- Not yet materialized
    vlan100 = Vlan(s, 100, name="VLAN 100", voice=True)

    # Since object is not materialized, performs
    # a POST request -- This method internally
    # makes a GET request right after the POST
    # Obtaining all attributes VLAN related
    vlan100.apply()

    # Now let's create another object, that we know already exists
    # inside of the Switch
    vlan1 = Vlan(s, 1)
    # Perform a GET request to obtain all data and materialize object
    vlan1.get()
    # Now, we are able to modify the objects internal attributes
    vlan1.voice = True
    # Apply changes
    changed = vlan1.apply()
    # If changed is True, a PUT request was done and object was modified

    vlan100.description = "New description, changed via pyaoscx SDK"
    vlan100.apply()
    # Now vlan100 contains the description attribute
    print("VLAN 100 description {0}".format(vlan100.description))

    # ===========================================================
    # ===========================================================
    # ===========================================================

    # APPROACH 2: IMPERATIVE FACTORY APPROACH
    # VLAN
    # Create Factory object, passing the Session Object
    factory = PyaoscxFactory(s)

    # Create Vlan object
    # If vlan is non-existent, Factory instantly creates it
    # inside the switch device
    vlan200 = factory.vlan(200, "NAME200")

    # Now the granulated approach could still be used.
    # Or an imperative method too

    # ===========================================================
    # ===========================================================
    # ===========================================================

    # More complex example using the OPEN GRANULATED APPROACH
    # Create an Interface object

    lag = Interface(s, "lag1")
    lag.apply()

    # Create a Vlan object

    vlan_1 = Vlan(s, 1)
    # In this case, now that the VLAN exists within the Switch,
    # a GET request is called to obtain the VLAN's information.
    # The information is then added to the object as attributes.
    vlan_1.get()
    # Interfaces/Ports added to LAG
    port_1_1_11 = Interface(s, "1/1/11")
    port_1_1_11.get()
    # Make changes to configure LAG as L2
    lag.admin = "down"
    lag.routing = False
    lag.vlan_trunks = [vlan_1]
    lag.lacp = "passive"
    lag.other_config["mclag_enabled"] = False
    lag.other_config["lacp-fallback"] = False
    lag.vlan_mode = "native-untagged"
    lag.vlan_tag = vlan_1
    # Add port as LAG member
    lag.interfaces.append(port_1_1_11)

    # Apply changes
    lag.apply()

    # ===========================================================
    # ===========================================================
    # ===========================================================

    # Same complex example using the IMPERATIVE FACTORY APPROACH
    # PLUS USING IMPERATIVE METHODS

    # Create the Interface object
    lag2 = factory.interface("lag2")
    modified = lag2.configure_l2(
        description="Created using imperative method",
        admin="up",
        vlan_mode="native-untagged",
        vlan_tag=1,
        trunk_allowed_all=True,
        native_vlan_tag=True,
    )
    # If modified is True, a PUT request was done and object was modified

except Exception as error:
    print("Ran into exception: {0}. Closing session.".format(error))

finally:
    # At the end, the session MUST be closed
    s.close()

Creating Custom Workflows

Similar to the provided example, you'll need to create your Session object in order to create a connection to the AOS-CX device. Alternatively, if you're hesitant in storing switch credentials in plaintext, you're able to use Python libraries to either get credentials at runtime or store them in an encrypted file using Vault.

Once you have your Session object you can begin configuring features of the CX switch using Classes and functions. The below example creates a Device object

In the below example you can see the Vlan and Interface Classes are used to create VLAN100 and configure an interface 1/1/14 with access to the newly created VLAN:

from pyaoscx.session import Session
from pyaoscx.vlan import Vlan
from pyaoscx.device import Device
from pyaoscx.interface import Interface

# OPTIONAL: Disable WARNINGS that occur when using insecure connection
# with self-signed cert
import urllib3
urllib3.disable_warnings()

# There are two approaches to workflows, both using the session.
version = "10.04"
switch_ip = "172.25.0.2"
s = Session(switch_ip, version)

# OPTIONAL: retrieve username & password through keyboard input
# at runtime, requires getpass4 python library
from getpass4 import getpass
username = input("Enter Username: ")
password = getpass('Password: ')

s.open(username, password)

switch = Device(s)

# Try block is used so that session closes even on error.
try:
    vlan100 = switch.vlan(100)
    
    # Example of creating an object and setting attributes to configure
    vlan100.voice = True
    
    # apply() must be called on the object to apply the configuration
    # vlan100.materialized will be true if configuration exists on switch
    # and application was successful 
    vlan100.apply()

    interface = switch.interface('1/1/14')

    interface.admin_state = "up"

    interface.apply()

    # using functions directly on an object will call apply() in the
    # background and therefore not necessary
    interface.configure_l2(
        vlan_mode="access",
        vlan_tag=int(vlan100.id)
    )


except Exception as error:
    print("Ran into exception: {0}. Closing session.".format(error))

finally:
    # At the end, the session MUST be closed
    s.close()