Cableplan Application

The Cable Plan module allows the programmer to easily import existing cable plans from XML files, import the currently running cable plan from an APIC controller, export previously imported cable plans to a file, and compare cable plans.

More advanced users can use the Cable Plan to easily build a cable plan XML file, query a cable plan, and modify a cable plan.

Using the Cable Plan

Invoking

The Cable Plan module is imported from cableplan.py which can be found in the acitoolkit/applications/cableplan directory.

It can be incorporated directly into a python script, or it can be used from the command-line.

>>>from cableplan import CABLEPLAN

When you want to create a cable plan from the current running topology of an ACI fabric, simply do the following:

>>>cp = CABLEPLAN.get(session)

Where session is an ACI session object generated using the acitoolkit. cp will be the cable plan object.

You can export that cable plan by opening a file and calling the export() method as follows:

>>>cpFile = open('cableplan1.xml','w')
>>>cp.export(cpFile)
>>>cpFile.close()

The cable plan will be written to the cableplan1.xml file.

Reading an existing cable plan xml file is equally easy.:

>>>fileName = 'cableplan2.xml'
>>>cp2 = CABLEPLAN.get(fileName)

Note that you don’t have to explicitly open or close the file. The get(fileName) method will take care of that for you.

Comparing cable plans is one of the more interesting cabablilities of the Cable Plan module and is very easy to do using the “difference” methods. When generating the difference between two cable plans, the module will return those items that exist in the first cable plan, but not in the second.

For example, assume that in the above example, the second cable plan read from the cableplan2.xml file does not have switch “Spine3” and the first cable plan does have it. The following example will print all of the switches in the first cable plan and not in the second.:

>>>missing_switches = cp1.difference_switch(cp2)
>>>for switch in missing_switches :
>>>    print switch.get_name()
Spine3

Similiarly, the following example will print all of the missing links:

>>>missing_links = cp1.difference_link(cp2)
>>>for link in missing_links :
>>>    print link.get_name()

To understand all of the differences between two cable plans it is necessary to compare them in both directions

>>>missing_links = cp1.difference_link(cp2)
>>>extra_links = cp2.difference_link(cp1)
>>>print('The following links are missing from the second cable plan')
>>>for link in missing_links :
>>>    print link.get_name()
>>>print('The following links are extra links in the second cable plan')
>>>for link in extra_links:
>>>    print link.get_name()

If multiple ports are specified in the link object with minPorts and maxPorts attributes (see Cable Plan XML Syntax below), it is possible that a link object in the first cable plan is only partially met by the link objects in the second cable plan. The remaining_need() method of the CpLink object.:

>>>missing_links = cp1.difference_link(cp2)
>>>for link in missing_links :
>>>   print('Link',link.get_name(), 'still needs',link.remaining_need(),'links to satisfy its mimimum requirement')

There is a similar method,

>>>missing_links = cp1.difference_link(cp2) >>>for link in missing_links : >>> print(‘Link’,link.get_name(), ‘still needs’,link.remaining_need(),’links to satisfy its mimimum requirement’)

There is a similar method,

>>>missing_links = cp1.difference_link(cp2) >>>for link in missing_links : >>> print(‘Link’,link.get_name(), ‘still needs’,link.remaining_need(),’links to satisfy its mimimum requirement’)

There is a similar method, remaining_avail() that returns the number of physical links the link object could match.

The remaining_need() and remaining_avail() values are reset when the difference_link() method is invoked.

It might be necessary to compare cable plans when the names of the switches are different, but the topologies are the same. This can easily done by simply changing the names of the switches that are different and then doing the comparisons.:

>>>switch = cp1.get_switch('Spine1')
>>>switch.set_name('Spine1_new_name')

This will automatically also fix-up all of the link names that are connected to the switch whose name is being changed. Note that this is also an easy way to change the name of a switch in a cable plan file. Simply read it in, change the switch name, and export it out. The following example will read in cable_plan2.xml, change the name of ‘Leaf1’ to ‘Leaf35’, and then export to the same file the modified cable plan:

>>>fileName = 'cable_plan2.xml'
>>>cp2 = CABLEPLAN.get(fileName)
>>>switch = cp2.get_switch('Leaf1')
>>>switch.set_name('Leaf35')
>>>f = open(fileName,'w')
>>>cp2.export(f)
>>>f.close()

Cable Plan from the Command Line

Invoking the cable plan application from the command line is very simple.

From the command prompt do the following::
> python cableplan.py -h

This will then return usage instructions that explain each of the command line options.

There are two primary functions that can be invoked from the command-line: ‘export’ and ‘compare’.

The ‘export’ function, selected with the ‘-e’ option, will create a cable plan by reading the state of the ACI fabric from the APIC controller. This cable plan will be either displayed on the monitor or, if a filename is specified, will be placed in a file. It will be in nicely formatted XML.

The ‘compare’ function will compare two cable plans. One of those must be in a file that is specified at the command line and the second one can come either directly from the APIC or from a second file. If only the ‘-c1 file_name’ option is used, then the content of file_name is compared to the actual running configuration of the ACI fabric.:

> python cableplan.py -c1 netwrk1.xml

If you want to compare two files, then both the ‘-c1 file_name1’ and ‘-c2 file_name2’ options must be used.:

> python cableplan.py -c1 netwrk1.xml -c2 netwrk2.xml

This comparison will list all of the links in the first cable plan that are not in the second and vice-versa.

Cable Plan XML Syntax

The cable plan XML looks like the following

<?xml version="1.0" encoding="UTF-8"?>
<?created by cable.py?>
<CISCO_NETWORK_TYPES version="None" xmlns="http://www.cisco.com/cableplan/Schema2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="nxos-cable-plan-schema.xsd">
   <DATA_CENTER networkLocation="None" idFormat="hostname">
      <CHASSIS_INFO sourceChassis="spine1" type="n9k">
         <LINK_INFO sourcePort="eth2/35" destChassis="leaf1" destPort="eth1/50"/>
         <LINK_INFO sourcePort="eth2/3" destChassis="leaf3" destPort="eth1/50"/>
         <LINK_INFO sourcePort="eth2/2" destChassis="leaf2" destPort="eth1/50"/>
      </CHASSIS_INFO>
      <CHASSIS_INFO sourceChassis="spine2" type="n9k">
         <LINK_INFO sourcePort="eth2/1" destChassis="leaf1" destPort="eth1/49"/>
         <LINK_INFO sourcePort="eth2/3" destChassis="leaf3" destPort="eth1/49"/>
         <LINK_INFO sourcePort="eth2/2" destChassis="leaf2" destPort="eth1/49"/>
      </CHASSIS_INFO>
   </DATA_CENTER>
</CISCO_NETWORK_TYPES>

The CHASSIS_INFO tag normally identifies the spine switches and the leaf switches are contained in the LINK_INFO. When the XML is read in, both leaf and spine switch objects will be created and the get_switch() and get_link() methods can be used to access them.

The LINK_INFO syntax also allows more flexible and loose specifications of the links. If the sourcePort or destPort attributes are left out, then any port on that corresponding switch can be used. The sourcePort and destPort attributes can also take port ranges, and lists as shown here:

<LINK_INFO sourcePort="eth1/1-eth1/15, eth1/20" destChassis =
"leaf3"/>

In addition, you can add minPorts and maxPorts attributes to specify the minimum number of ports or maximum number of ports when multiple are defined.:

<LINK_INFO sourcePort="eth2/3, eth3/4 - eth3/10",
destChassis="leaf2", destPort="eth1/1 - eth1/8", minPorts=3,
maxPorts=5>

If minPorts is omitted, the default will be 1. If maxPorts is omitted, the default will be unlimited.

When comparing two cable plans using the difference_link() method, if the minimum number of links in the first cable plan can be met with second cable plan, then the difference will show no difference. Note that it is possible that requirements of several links specified in one cable plan may be met by one or more links in the other. Basically, the difference is calculated such that the minimum requirements of the first cable plan are met without exceeding the maximum capacity of the second cable plan.

Cable Plan API Reference

class cableplan.CpPort(port_set)

This class holds the information for a link’s port. Since the port can be a single port, a list or a range, putting it in a class allows more flexible operations on it.

list()
name()
remove_available_port(port)
reset_accounting()
export(chassis, level)

Will return string of XML describing the LINK_INFO. It will use ‘chassis’ to determine which is the source chassis so that it will be omitted from the XML and the other chassis will become the destination. ‘level’ is the indentation level.

Parameters:
  • chassis – Chassis that is the parent of the LINK_INFO xml
  • level – Indentation level
Returns:

str

get_name()
has_port_in_common(link)

Returns True if link has any ports that match self. It will compare all ports included expanded lists of port sets.

Parameters:link – link to check to see if matches, or overlaps, with self
Returns:Boolean
is_connected(switch1, switch2=None)

Returns True if switch1 is one of the switch endpoints of the link and switch2 is unspecified otherwise is will return True if both switch1 and switch2 are switch endpoints of the link. If switch1 is the same as switch2, it will return False.

Parameters:
  • switch1 – first switch to check if it an end-point of the link
  • switch2 – optional second switch to check if it an end-point of the link
Returns:

True if switch1 (and optional switch2) is an end-point of the link

This will match-up link1 and link2 and increment the reference count in each link for each of the matches that happen. It will do this until the minimum number of links has been reached for link1. It will return the number of matches that occurred.

Parameters:
  • link1 – first link of type CpLink that is part of the matching
  • link2 – second link of type CpLink that is part of the matching
Returns:

number of matches that occured.

order()

Calculates the order of the link defined by the maximum number of physical links this link can represent

Returns:int
remaining_avail()

returns the remaining number of physical links available to match against The parameters used to calculate this value are reset by the reset_accounting() method which is typically invoked when invoking a difference_link() method on the CABLEPLAN parent object.

Returns:int
remaining_need()

returns the remaining number of physical links needed to match against self to satisfy requirements. The parameters used to calculate this value are reset by the reset_accounting() method which is typically invoked when invoking a difference_link() method on the CABLEPLAN parent object.

Returns:int
reset_accounting()

Resets account on the source and dest ports as well as reference count

class cableplan.CpSwitch(name, chassis_type=None, spine=False, parent=None)

Bases: object

class holding a switch

export(level)

returns a list of CP_LINKS from the parent CABLEPLAN that are connected to self.

Returns:list of CP_LINKS
get_name()

Gets the name of the chassis.

Returns:str
get_type()

Gets the chassis type. Examples of chassis types are ‘n7k’ or ‘n9k’

Returns:str
is_spine()

Checks if the ‘spine’ flag is set.

Returns:True if the spine flag is set, otherwise False
merge(new_switch)

Merges the content of new_switch with self. If self has variables set, then they will not be changed. If they have not been set, then they will be assigned the value from new_switch.

Parameters:new_switch – switch object to merge with self
set_name(name)

Sets the switch name. This will over-ride any preexisting name. Note that this new name will now become part of the link name for all the links attached to this switch.

Parameters:name – name string to set in the switch
set_parent(parent)

Sets the parent of the switch. Parent must be of type CABLEPLAN. If a parent CABLEPLAN was already set and it is differnt from parent, then an error is raised.

Parameters:parent – parent object of type CABLEPLAN
class cableplan.CABLEPLAN(version=None)

Will add a link to the CABLEPLAN. Duplicates will not be allow, but overlapping will be.

Parameters:new_link – Link to be added of type CpLink
Returns:None
add_switch(new_switch)

This will new_switch to the CABLEPLAN. If the switch already exists, it will merge the new_switch with the existing one. It will also set the parent of the switch to be the CABLEPLAN. It will return the final switch, i.e. new_switch if no merge occurred or the newly merged switch if a merge did occur.

Parameters:new_switch – switch to be added of type CpSwitch
Returns:CpSwitch
delete_switch(old_switch)

returns a list of links that are in self, but not in cp.

Parameters:cp – cable plan of type CABLEPLAN
Returns:list of CpLink
difference_switch(cp)

Will return a list of switches that are in self, but not in cp.

Parameters:cp – cable plan
Returns:list of CpSwitch
exists_switch(switch)
export(out_file=None, level=0)

Will generate XML text of the entire CABLEPLAN and return it as a string. If out_file is specified, it will write the XML to that file. out_file should be opened for writing before calling this method. ‘level’ specifies the amount of indentation to start with.

export_data_center(level=0)

Will generate the XML of the CABLEPLAN with DATA_CENTER as the root. This will then be returned a string. ‘level’ specifies the indentation level to start with.

Parameters:level – optional indention level, integer
Returns:string that is the DATA_CENTER xml
classmethod get(source)

This will get input a cable plan from ‘source’. If source is a string, it will get the cable plan from XML in a file whose name is source. If it is a Session, it will read the corresponding APIC to get the cable plan.

Parameters:source – filename of type string or Session of type Session
Returns:CABLEPLAN

Returns a list of links. If switch is unspecified, it will return all links. If switch is specified, it will return all of the links that are connected to switch. If both switch1 and swithc2 are specified, it will return all links that are connected between the two switches.

Parameters:
  • switch1 – optional first switch of type CpSwitch
  • switch2 – optional second switch of type CpSwitch
Returns:

list of links of type CpLink

get_spines()

Will return list of switches that are spines

Returns:list of CpSwitch
get_switch(switch_name=None)
reset_accounting()

clears the refernce count on each link :rtype : None

returns a sorted list of links between switch1 and switch2. They are sorted by specificity from most specific to least specific. The specificity is determined by which list of ports is the minimum between source and destination and which is the minimum across links. :rtype : list :param switch1: :param switch2: