Pathway Engineering’s Configuration Generator¶
Eliminating mistakes by making a developer responsible to fix them for you!
Introduction¶
When you’ve had to complete as many equipment upgrades as we have, the process of having to generate cutsheets and new configuration files is tedious and, well, kinda boring. “So let’s automate it instead,” someone said. Lo and behold, we delivered LIKE A BOSS.
The Config Generator is designed to take in existing configuration files, jack-to-port documentation, current device model and target model in order to speed up the process of pre-configuring equipment for Next-Gen project deployment. Hopefully we will get around to creating a Cisco-to-Dell and vice-versa extension, but we’ll that to Kent - because he slaves away his free time to work for some strange reason.
Contents:
Getting started with the awesome configuration generator¶
Installation and Preparation¶
Before you begin, a few packages are necessary to download and install. You can do this via pip
for easy installation. You can simply type pip install -r requirements.txt
(or pip install -r dev-requirements.txt
if you plan on contributing).
Dependencies¶
natsort==5.0.1
ciscoconfparse==1.2.40
colorama==0.3.7
ipaddr==2.1.11
dnspython==1.14.0
openpyxl==2.3.5
et-xmlfile==1.0.1
jdcal==1.2
Developer Dependencies¶
alabaster==0.7.8
Babel==2.3.4
ciscoconfparse==1.2.40
colorama==0.3.7
dnspython==1.14.0
docutils==0.12
et-xmlfile==1.0.1
imagesize==0.7.1
ipaddr==2.1.11
jdcal==1.2
Jinja2==2.8
MarkupSafe==0.23
natsort==5.0.1
openpyxl==2.3.5
Pygments==2.1.3
pytz==2016.4
six==1.10.0
snowballstemmer==1.2.1
Sphinx==1.4.4
sphinx-rtd-theme==0.1.9
Directory Structure¶
In order to run the script, several folders need to be created:
configconverter
|\_ configs
|\_ cutsheets
\\_ output
\_ templates
Invoking the module from CLI¶
The module includes an if __name__ == '__main__':
statement so it can be
called from the directory itself. You may copy it directly, but we’ve included
it here (with the import
) for convenience:
from configconverter import SwitchConfigGenerator
if __name__ == "__main__":
oldconfig, newconfig = get_configs()
switch_type = get_switch_model()
hostname = force_user_input("Enter hostname of new switch: ").upper()
outputfile = input(
"Enter output file name (default - " + hostname + ".txt): ")
outputfile = outputfile if outputfile else hostname + ".txt"
createconfig = ("n" not in input(
"Generate full config file?[Y|n]: ").lower())
blades, nojacks, newjacks = migrate_ports(
oldconfig, newconfig, hostname, switch_type)
# Add ip dhcp snooping later! Adding it immediately after interfaces
# causes a bug if trying to use file as startup-config
vlans = vlan_extract(oldconfig, newconfig, feed_ports_regex[
switch_models[switch_type]], createconfig)
setup_feeds(newconfig, switch_type, blades, vlans)
interfaces_for_review(newconfig, nojacks, newjacks)
set_voice_vlan(oldconfig)
access_cleanup(newconfig)
trunk_cleanup(newconfig)
# This must be run AFTER trunk_cleanup()
if not switch_models[switch_type] == "3560":
remove_mdix_and_dot1q(newconfig)
if createconfig:
baseconfig = ".txt"
if (switch_type == len(switch_models) - 1):
baseconfig = "baseconfig.txt"
else:
baseconfig = switch_models[switch_type] + "base.txt"
newconfig.prepend_line("!")
newconfig.prepend_line("hostname " + hostname)
newconfig.prepend_line("!")
newconfig.commit()
extract_management(oldconfig, newconfig)
add_snooping(newconfig, vlans)
newconfig.append_line("!")
with open(template_dir + baseconfig, "r") as b:
for line in b:
newconfig.append_line(line.rstrip())
newconfig.commit()
file_export(outputfile, newconfig)
Note
You are still responsible for including the module directory in the search path
Order of operation¶
Since the config generator is far from perfect – unlike myself – there are a few functions that can wreck havoc on your configuration output if you’re not careful. (Using the example layout from the script itself works fine.) When you import the module for use in other scripts, be sure to:
migrate_ports()
before vlan_extract()
¶
vlan_extract()
will also prune any VLANs that are not assigned to
edge ports so as to clean out the VLAN database.
trunk_cleanup()
before remove_mdix_and_dot1q()
¶
If ports are configured with switchport mode trunk
but still contain
commands for access ports, all encapsulation configuration will be removed
Call add_snooping()
last¶
Adding ip dhcp snooping
too early will cause the switch to believe that
it should be applied to an interface. Since it is not a valid interface
command, it discards it from the startup-config. It should be invoked last
with a least one function called in-between it and migrate_ports()
,
that way a buffer is put between the configuration generated by them.
Note
It should also be called after migrate_ports()
so that
pruned VLANs are not added to the list
Example¶
Calling too early:
interface vlan 300
ip address 10.23.21.4 255.255.255.128
no ip route-cache
! The switch will mistakenly apply the next line to VLAN 300 and discard
ip dhcp snooping vlan 24,109,209,309,318,483,509,609,620,651,709,902,985,1902
no ip dhcp snooping information option
ip dhcp snooping
Just need port configuration? I gotchu¶
If the managed device is setup correctly but ports have had a shift in configuration, you can generate just the list of ports so as to not risk accidentally overriding any management settings. You will still have the option to configure feeding ports too, if desired. The following snippet would be sufficient:
from configconverter import SwitchConfigGenerator
if __name__ == "__main__":
oldconfig, newconfig = get_configs()
switch_type = get_switch_model()
hostname = force_user_input("Enter hostname of new switch: ").upper()
outputfile = input(
"Enter output file name (default - " + hostname + ".txt): ")
outputfile = outputfile if outputfile else hostname + ".txt"
createconfig = ("n" not in input(
"Generate full config file?[Y|n]: ").lower())
blades, nojacks, newjacks = migrate_ports(
oldconfig, newconfig, hostname, switch_type)
# Add ip dhcp snooping later: adding it immediately after interfaces
# causes a bug if trying to use file as startup-config
vlans = vlan_extract(oldconfig, newconfig, feed_ports_regex[
switch_models[switch_type]], createconfig)
setup_feeds(newconfig, switch_type, blades, vlans)
interfaces_for_review(newconfig, nojacks, newjacks)
set_voice_vlan(oldconfig)
access_cleanup(newconfig)
trunk_cleanup(newconfig)
file_export(outputfile, newconfig)
SwitchConfigGenerator API¶
-
SwitchConfigGenerator.
access_cleanup
(newconfig)¶ Remove trunk configuration for all ports set to access mode
Parameters: - oldconfig – CiscoConfParse object of existing configuration file
- newconfig – CiscoConfParse object, representing the “new” config file
-
SwitchConfigGenerator.
add_snooping
(newconfig, vlans)¶ Add DHCP snooping commands to new configuration file
Parameters: - newconfig – CiscoConfParse object, representing the “new” config file
- vlans – List of VLANs to add
-
SwitchConfigGenerator.
add_voice_vlan
(voicevlan, newconfig)¶ Add voice VLAN to access ports that do not have it
Todo
Remove print statements when interfaces_for_review is completed
Parameters: - voicevlan – a VLAN represented as a string or int
- newconfig – CiscoConfParse object representing the “new” configuration file
-
SwitchConfigGenerator.
condensify_ports
(ports)¶ Turn a collection of ports into a Cisco-formatted range
Todo
Altering format depending on “new” switch model
Parameters: ports – List of port names Returns: Ports in a Cisco-formatted range Return type: String
-
SwitchConfigGenerator.
extract_management
(oldconfig, newconfig)¶ Extract the management VLAN and add it to new config file
Assuming the target equipment is a layer 2 switch with only one management VLAN, the VLAN config is extracted and the option to retain IP information is provided. ip tacacs source-interface <VLAN> is added but only necessary for a 4506; this command will be ignored on all other models.
Parameters: - oldconfig – CiscoConfParse object of existing configuration file
- newconfig – CiscoConfParse object, representing the “new” config file
-
SwitchConfigGenerator.
file_export
(outputfile, newconfig)¶ Save current configuration to a file
Exports to the directory defined by internal/global var ‘output_dir’
Parameters: - outputfile – Desired file name
- newconfig – CiscoConfParse object, representing the “new” config file
-
SwitchConfigGenerator.
force_user_input
(display, expect='')¶ Enforce that the user input at least one character
Parameters: - display – String to display as the input prompt
- expect – Regex string representing the required format of the response before returning to caller. Defaults to an empty string (match any)
Returns: User’s input
Return type: String
-
SwitchConfigGenerator.
get_configs
()¶ Set the configuration file to pull data from Prompts user for file name
Returns: oldconfig – Existing/source configuration file newconfig – Container for the new device’s configuration Return type: CiscoConfParse, CiscoConfParse
-
SwitchConfigGenerator.
get_switch_model
()¶ Prompt user to select model from compatible list
Returns: The user’s input as the internal switch_models index Return type: Int
-
SwitchConfigGenerator.
get_vlan_list
(oldconfig, regex)¶ Retrieve all VLANs from the old configuration file and return a list.
(This is intended for future use in cross-platform conversions.)
Parameters: - oldconfig – CiscoConfParse object of existing configuration file
- regex – Regex string used to determine if port is a feed
Returns: All VLANs defined, sorted in ascending order
Return type: List
-
SwitchConfigGenerator.
interfaces_for_review
(newconfig, nojacks, newjacks)¶ Searches for interfaces on the “new” device with non-standard configs to be reviewed manually.
Searches for statically set PoE, duplex, operating speed, no defined switchport mode
Parameters: newconfig – CiscoConfParse object representing the “new” configuration file
-
SwitchConfigGenerator.
is_ip
(addr)¶
-
SwitchConfigGenerator.
migrate_ports
(oldconfig, newconfig, hostname, switch_type)¶ Map and transfer configuration settings of old ports
Searches for Excel workbooks in the ./cutsheer_dir/ directory. The “cutsheet” files are generated by TurboClerk which are pulled from Netdoc.
As of May 2016, Carlos Bassett has set a standard for worksheet layouts and file names, as follows:
- The spreadsheets must have each tab nammed from the source switch and the first column MUST be the port name.
- Any jack associated to this port must be in the row, otherwise configuration will not be transferred.
- The file with the current port-jack mappings must have the building code in the name, along with “as is”.
- The file with the future port-jack mappings must have the building code in the name, along with “to be”.
- The file with the future port-jack mappings MUST begin listing ports in the third row.
Note that this function has the potential to break if corresponding jacks are found from a different existing switch. A future workaround of loading the switch it is found from has been added as a TODO
Todo
Remove print statements when interfaces_for_review is completed
All returned variables are intended for printing out warnings/notices/debugging
Parameters: - oldconfig – CiscoConfParse object of existing configuration file
- newconfig – CiscoConfParse object, representing the “new” config file
- hostname – Name of the new switch
- switch_type – the index of switch_models that represents the “new” switch model
Returns: _blades_ – Detected blade numbers in the stack _nojacks_ – Port names on the new switch that do not have a jack associated with them _newjacks_ – Port names on the new switch that have jacks associated to them, however they do not exist in any As-Is spreadsheets
Return type: (Set, List, List)
-
SwitchConfigGenerator.
no_files_found
(directory)¶ Allows the user to move files to correct directory or exit early
Parameters: directory – The folder in which the files should be located
-
SwitchConfigGenerator.
remove_mdix_and_dot1q
(newconfig)¶ Remove MDIX and dot1q from all interfaces
Note
Should be run after trunk_cleanup()
Keyword arguments: newconfig – CiscoConfParse object, representing the “new” config file
-
SwitchConfigGenerator.
set_voice_vlan
(oldconfig)¶ Select and add a voice VLAN to add to access ports
Parameters: oldconfig – CiscoConfParse object of existing configuration file
-
SwitchConfigGenerator.
setup_feeds
(newconfig, switch_type, blades, vlans)¶ Configure feedports
Allows the user to define as many feedports as desired. Checks the validity of the port name as defined by a regex string.
Parameters: - newconfig – CiscoConfParse object, representing the “new” config file
- switch_type – The representation of the switch model in the form of the switch_models index
- blades – A Set of all blade numbers in the stack
- vlans – – A List of all VLANs transferred to the new configuration file
-
SwitchConfigGenerator.
trunk_cleanup
(newconfig)¶ Remove access mode configuration on trunk ports
Removes access/voice vlan configs, spanning-tree portfast, and no snmp trap link-status
Todo
Detect and remove VLANs from VLAN ranges
Parameters: newconfig – CiscoConfParse object representing the “new” configuration file
-
SwitchConfigGenerator.
vlan_extract
(oldconfig, newconfig, regex, genconfig=False)¶ Retrieve all VLANs from the old configuration file
Automatically detects if certain VLANs will not be used and will offer to prune them.
Note
For pruning to work, this _must_ be called before
setup_feeds()
Parameters: - oldconfig – CiscoConfParse object of existing configuration file
- newconfig – CiscoConfParse object, representing the “new” config file defaults to None
- regex – Regex string used to determine if port is a feed
- genconfig – A boolean representing if a full config will be generated: If True, all VLANs will be added to the new config file. Defaults to False
Returns: All VLANs defined, sorted in ascending order
Return type: List