Building Python dictionaries from CLI

So we’ve looked at running CLI commands using python, and you might be like “so what.” (If you haven’t been following along you may need to catch up some) Yes there is some benefit in automating logging into a controller, running some commands, and saving the response to a file. Maybe even running the script before and after changes. That could save some time write change controls and running validations. But what I want to show you is how to get that response in python and turn it into a useful dictionary.

TextFSM was created by Google and is installed as part of Netmiko! So if you are using Netmiko like I showed in my previous post, you can start leveraging this right away. The only caveat is there are not many templates for wireless devices in the current repository. Any device that works with Netmiko could work with TextFSM but templates might need to be built. I have built quite a few templates for cisco aironet and will share my github repo. I have submitted them to be included in TextFSM but I am waiting for a review. 

You can get the repo for TextFSM here. 
https://github.com/networktocode/ntc-templates
The commands included are:

show cdp neighbors detail
show ap config general
show ap summary
show sysinfo

My copy of the repo is here.
https://github.com/timjsmith24/ntc-templates-with-ciscowlc.git
The commands I have added are the following:

show 802.11a cleanair config
show 802.11b cleanair config
show network assurance summary
show mdns service summary
show advanced probe
show load-balancing
show interface summary
show inventory
show wlan apgroups
show advanced fra
show band-select
show network summary
show trapflags
show sessions
show wlan summary
show 802.11a
show 802.11b
show time

So for now I will go through adding my version with the additional cisco aironet templates. To run the cli command and parsing the results is actually a really easy process. 

By default, Netmiko is going to look look for an index file for the templates in the specific folder /Users/<user name>/ntc-templates/templates. So if you would like to install the repo there, in the terminal window type ‘cd ~’ this will take you to your root directory. Then type git clone https://github.com/timjsmith24/ntc-templates.git.

Once you have it installed you will need to fix a couple things in the index file. Here is a link to instructions on how to do this will an explanation as to why. https://bmatwifilabs.wordpress.com/2019/09/21/issues-with-textfsm-index-file/

Once that is fixed you are ready to go.

All you need to do to take advantage of this is add ‘use_textfsm=True’ after the command when sending with Netmiko. As long as a template exists and the index file is correct the result will be a dictionary! 

output = netconnect.send_command(cmd, use_textfsm=True)

So lets look at an index file. 

If you open the cisco_wlc_ssh_show_inventory.template file (using sublime) you will see at the top a bunch of Values

Value MAC (([\d1-9A-F]{2}\:?){6})
Value MAX_AP_COUNT (\d+)
Value TYPE (\w+)
Value DESCR (.+?)
Value PID (\w+\-\S+\-\S+)
Value VID (\S+)
Value SERIAL_NUMBER (\S+)

The strings after ‘Value’ are the variables defined for the template. These will be the keys of the dictionary. Following that in the ‘()’ are the regex the template will be looking for in the CLI response.

After that you will see a line with ‘Start’ and then the lines under that with a 2 space indent followed by a ^. These lines are regex of a line in the CLI response.

Start
^Burned\-in\sMAC\sAddress\.*\s${MAC}\s*$$
^Maximum number of APs supported\.*\s${MAX_AP_COUNT}\s*$$
^NAME:\s\”${TYPE}\”\s+,\sDESCR:\s${DESCR}\s*$$
^PID:\s${PID},\s+VID:\s${VID},\s+SN:\s${SERIAL_NUMBER}

What happens is the template will go through the response and look for any line that matches the first regex line. Once it finds one it will look for a line that matches the second regex line and so forth. 

Ok so let’s go through the first line. It will look for the actual characters ‘Burned’ the the ‘\-‘ says to look for an actual ‘-‘ character, the ‘\s’ says to look for a single space. the ‘\.*’ means to look for 0 or more periods (.)s. So there could be 0 ‘.’, 1′.’ or 20 ‘.’s. The ${MAC} will look for the regex defined for MAC Value. ‘\s*$$‘ means 0 or more spaces followed by an end of line. If a line is found with that entire regex then MAC will become the part of the regex found with ${MAC}.

Building your own templates can take a little practice and time, but once you get familiar with regex you can build them pretty fast. There are ways to build lists and search for identical lines and other features. If you look at the README file in the /Users/<user name>/ntc-templates/ there are more instructions there. Including how to name the files, update the index file and more. 

Advertisement

Issues with TextFSM index file

There is an issue within the index file of TextFSM when using the cisco wlc commands in netmiko. I believe they are aware of the issue as I think I remember seeing a note somewhere in the testing that this needs to be fixed.

If you are not familiar with TextFSM it was created by google and allows you to parse text files and get structured data. I use this with the netmiko module in python to run CLI commands on Cisco Wireless controllers and build dictionaries from the responses.

You can get the repo for TextFSM here.
https://github.com/networktocode/ntc-templates

If you are using this version of TextFSM there are only 4 commands for Cisco Wireless Controllers.

show cdp neighbors detail
show ap config general
show ap summary
show sysinfo

When you clone the repo the index file is in ntc-templates/templates directory with all of the templates.

In the index, lines 237-240 are the 4 WLC commands and they look like this

cisco_wlc_ssh_show_sysinfo.template, .*, cisco_wlc_ssh, sh[[ow]] sysi[[nfo]]

Netmiko will not recognize this when scanning the index file. To fix this change the ‘cisco_wlc_ssh‘ to just ‘cisco_wlc‘.

The other issue is with the show ap config general command. In order to pass googles automated tests to add additional templates the actual AP name was removed from the command. In order to fix this you will need to add ‘ (\S+)’ to the end of this line to be able to put the AP name. This added regex to end of the command looking for any string of characters without a space.

cisco_wlc_ssh_show_ap_config_general.template, .*, cisco_wlc_ssh, sh[[ow]] ap con[[fig]] ge[[neral]]

Afterwords you should have something that looks like this.

cisco_wlc_ssh_show_ap_config_general.template, .*, cisco_wlc, sh[[ow]] ap con[[fig]] ge[neral] (\S+)

Now if you’ve made it this far in the post I’d image you are interested in using TextFSM specifically with Cisco WLCs. If that’s the case you should check out my copy of the repo here. https://github.com/timjsmith24/ntc-templates-with-ciscowlc.git

The index file in my repo has the fix for the ‘cisco_wlc_shh’ but you would still need to fix the command for ‘show ap config general’ adding the regex to get the AP name. In my repo I have added templates and updated the index files for this list of Cisco WLC commands.

show 802.11a cleanair config
show 802.11b cleanair config
show network assurance summary
show mdns service summary
show advanced probe
show load-balancing
show interface summary
show inventory
show wlan apgroups
show advanced fra
show band-select
show network summary
show trapflags
show sessions
show wlan summary
show 802.11a
show 802.11b
show time

I also have a for of the original to get these commands added to the main repo but it is still waiting for a review.

I also have about 18 more templates that I need to build the test info for before I can submit them. So if you are looking for something specific, commit here or hit me up on twitter @timjsmith24 and see if I have it done already.

Python Issuing CLI commands over SSH

Something that I do frequently with python is establish an SSH connection to a wireless controller (or AP) and run CLI commands, collecting the response then preforming actions on the data collected. Sometimes it’s issuing other commands on the device – for example rebooting or clearing memory because a specific bug has been identified in the logs. Sometimes the script will send out an email or save a file with the collected data. I hope to lay out the foundation of this as it can greatly improve the time it takes to collect data from a device or multiple devices.

If you have been following along we covered creating a virtual environment (venv). The module that we will want to install in the venv is netmiko. So you can activate the venv by entering source venv/bin/activate (venv being the name of the virtual environment). Once activated run pip install netmiko.

Once it finishes installing you can run pip freeze which will show all of the modules that are installed. As you can see multiple modules were installed with netmiko. We are not going to go into details of these, but I will do a post on TextFSM in the near future. Using textfsm you can easily parse the response from the CLI command and can be extremely useful.

One of the big concerns using python scripts to log into controller is password management. You don’t want to have the password stored in clear text in the python script. It would be too easy to read the python file and get the passwords. There are different ways around this and it just depends on how you want to handle that. I will have a separate post showing a couple of ways that I frequently use. For this post we will request that info from the user.

Unfortunately, there isn’t a way to test receiving user input in sublime text, as I write the script I usually will set the variable to a specific value for testing in sublime text then once the script is fully written I will add the user input to allow assignment of the variable and test using iTerm.

But for this post we will use the python interpreter in terminal, which can be a great way to test code and learn how things are working with python. So in the terminal window running the virtual environment type in python. You should get a prompt with 3 >’s like below.

(venv) Sherlock:CLI Sherlock$ python
Python 3.7.4 (default, Jul 9 2019, 18:13:23)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type “help”, “copyright”, “credits” or “license” for more information.
>>>

Let’s start with accepting the user name from the user.
username = input(“please enter the username: “)
This will prompt the user with “please enter the username:” and have the curser waiting for the user to enter the name.

This will display what the user types as they type. This works great for most things but you still don’t want to have the password displayed on the screen when the user types. For this we will use the module getpass. So type import getpass then type pw = getpass.getpass(prompt=’please enter password: ‘). Now when prompted you can enter the password without it being echoed on the screen.

Ok so now we have the username and password stored in the script. Let’s define the device. For this I will be logging into a Cisco controller with IP address 192.168.1.11. You could use the IP address or dns name of the controller. I am just going to define it in the script instead of asking the user to input it. host = ‘192.168.1.11’

Ok now that all the information we need to create our SSH session is in the script we’ll create an object that contains all of these elements. The object we are going to create is called a dictionary. A dictionary is a collection of unique ‘keys’ with ‘values’ that are tied to those keys and or this dictionary the type of ‘keys’ will be strings. In python you define a dictionary by adding the keys and values inside of {} brackets. Netmiko will take this dictionary and create the session with the controller. Netmiko is expecting certain keys in this dictionary like ‘ip’, ‘username’, ‘password’, and ‘device_type’. So the dictionary we are going to use will look like this.

device = {
‘ip’:host,
‘username’:username,
‘password’:pw,
‘device_type’:’cisco_wlc’,
‘banner_timeout’:8
}

Notice that the all the keys are enclosed in ‘ ‘, making them a string type, then followed by a :, and then the value. Most of these values are not enclosed in ‘ ‘ meaning that they are variables that we already defined. The value for ‘device_type’ we had not defined previously, so I enclosed it with ” to make it a string but you could easily make that a variable as well if you are using different device types. I also added banner_timeout that you might not need. I’m not sure what the default value is for that but I had to change it to 8 seconds to be able to log into my lab controller. Each key:value pair in the dictionary is separated by a comma. A dictionary does not need to be on separate lines like above but can all be on one line like device = {‘ip’:host. ‘Password’:pw, ….}

Ok, now we need to import Netmiko. I typically add these 2 lines to have some exceptions incase of any errors while trying to connect.

from netmiko import Netmiko
from netmiko.ssh_exception import NetMikoAuthenticationException, NetMikoTimeoutException

We are going to try and establish the connection. To do this will with type in try:

We have not discussed spacing indentation in python, but indentation is very important when using code blocks. Code blocks could be the body of a function, loop, etc. or in this case a try block where python with try the code for errors and an except block that lets you handle the errors. Each line in the code block will need to have matching indentation and if there is nested code blocks indentation can be down further to the right.

So on the line after the try: you will need to indent the line. I personally just use a tab key to indent but many people will use 4 or 5 spaces.

After the indent we will put in the block of code that we want to try. In our case we are just putting in 1 line. netconnect = Netmiko(**device)
This passes the device object to the Netmiko module and creates the connection.

The next block of code will be the except block. except (NetMikoAuthenticationException, NetMikoTimeoutException): (remember to remove the previous indentation) Following the except line the next line will be indented and for this exception it will just print that if failed to connect to the host and exit out of the script. This will be the 2 lines for that. The first print(‘Failed to connect to {}’.format(host)) and the second exit()

This whole section together looks like the below.

It may take a second to establish the connection but once it connects will return to the 3 >’s prompt.

Now that the connection is established we can send commands and save the output to a variable with output = netconnect.send_command(‘show sysinfo’) then print to the screen with print(output). Once done with the connection we will need to close the session. This can be down with netconnect.disconnect()

Getting used to using variables and dictionaries will go far with Python. For example you could make a variable for a command like cmd = ‘show inventory’ and put that in the send command output = netconnect.send_command(cmd) or you could make a list object with a list of commands. In python you define a list by placing the objects in [] brackets. So for list of commands we could do cmds = [‘show sys info’, ‘show inventory’] Notice that the strings are separated by a ,.

If you had a list of commands you would need to send then 1 at a time. To do this you could create a For loop. this would run a block of code ‘for’ each element in a list. for cmd in cmds: Then inside that command block (don’t forget to indent) add the 2 lines output = netconnect.send_command(cmd) and print(output).

If you are following along you might be… well that’s cool but the output variable that we did is just a big string with the returned response… well yes, but next time I will show you the powerful tool textFSM that will allow you to parse the useful information from the responses and create a dictionary from that information.

Stay tuned.

If you would like to see the output of the code used today you can see it on github here in example_1.py

Python in virtual environment

I have recently started using virtual environments for Python development. This is something that is supposed to be “best practices” so figured I should try and adjust my workflows to using them. Some of the advantages of using environments are version control and testing for the modules as well as python itself.

When using a virtual environment each module used will need to be installed. So before we go farther with different modules I use for Wireless networking we will cover how to build virtual environments (venv) and how to use those venv’s inside of sublime text.

So if you followed along the last couple blogs you should have used homebrew to install python3 and used pip3 to install the snmp module. The next module we will install with virtualenv.

pip3 install virtualenv

Now you can create a virtual environment for you python script. So let’s go to the folder where we are going to write our python script in terminal. A quick way to get there if you are not familiar with terminal is to go to the folder location in a finder window. In terminal type ‘cd ‘, then you drag the folder from the finder window into terminal and hit enter. This will take you to that location in terminal.

Now that we are in the ‘project folder’ we can create the virtual environment. you can do this by entering virtualenv venv ‘venv’ becomes the name of the virtual environment and can be changed to something else if you would like. To specify which version of python you would like to use you can add ‘–python=python3’. For this example we will create a virtual environment for python 3.

virtualenv venv –python=python3

You will see that this creates a folder called venv in the CLI folder. When we are ready to use this we will need to activate it. You can do this in the terminal by entering

source venv/bin/activate

once you’re running in the venv you can install the needed modules. I usually keep a text file if required modules in the project folder.

to deactivate you just type deactivate

In sublime you can import your project folder which can make it easier to work within that folder.

But if you wanted to use this environment in sublime text you would need to install (I covered installing Sublime modules here) Virtualenv in Package Control.

Now that its installed you will need to change the build System to ‘Python + Virtualenv’

To active the Virtual environment you can go to the command Palette and search for Virtualenv. You’ll see options including activate and deactivate. You may need to add the directory, which is another option in the command palette.

You can also create the virtual environment right from there by selecting Virtualenv: New.

When creating a virtual environment in sublime you have to enter the file path and filename in the bar that pops up on the bottom and then select which version of python you want to use for that environment.

But I have not used that, I have been using iTerm for creating my venv and installing modules in the venv.

SNMP with Python

Installing modules

In order to get started with using SNMP in Python you will need to install some modules. This will allow you to use code that others have written to perform some of the low level tasked needed.

Pip is a package installer for python that is installed when you install python. If you followed my previous post and installed python3, to install modules for python3 you will need to use pip3. Using pip to install a module is super easy. All you need to do is type “pip3 install <module name>”

So, for SNMP you will need to install the pysnmp module

pip3 install pysnmp

AP group validate using SNMP

With that installed, let’s get started. We’ll go ahead and create a script that uses SNMP to get the AP names and AP groups of all the APs on a cisco controller, create a dictionary, save the dictionary as a json file, and then check to see if the AP group is correct based on the AP name.

For this example we are going to say that APs are named in the format of AP[building abbreviation]-[3 digit AP number]. For example, APBA-010 is AP 10 in Building A.

Let’s make it so we can give the script a list of controller ip addresses.

#!/usr/bin/python3
from pysnmp.entity.rfc3413.oneliner import cmdgen

Here we are adding the shebang line to define the location of the interpreter (Python3) then we are telling this script to use the previously installed module

Next, we will define an object called ‘cmdGen’ that we will use to perform the SNMP walks with.

cmdGen = cmdgen.CommandGenerator()

Functions

Python uses functions for any blocks of reusable code. So, let’s started with creating the main function. This will be where the script logically starts when ran.

To create a function in python you define the name and then pass any data (parameters). Since main is where the script is starting, we will not need to pass any data. You will also need to put these 2 lines at the bottom of the script. This is what will call the main function.

def main():

if __name__ == '__main__':
      main()

Every part of the code that is inside of this function will need to be indented. Indentation is very important with python and can be a bit of a pain. I usually use tabs to indent as it’s easy to just hit the tab key then hitting the space ‘x’ amount of times.

The first thing we will do in main() is create an empty dictionary object called aplist and create a list called wlclist that will be the ip address of the controller. You could add multiple controllers in the list by separating the strings with a comma.

aplist = {}

wlclist=['10.142.175.68']

Hopefully you remembered to indent those with a tab. Sublime will usually do this for you the best that it can but you should always be aware of the indentation.

We are going to use a for loop to go through all the controllers in the wlclist and call a function (that hasn’t been created yet) passing the function the wlc object. This function is going to return a dictionary of the APs on that controller. We will add that response to the aplist dictionary we created. Let’s call this function controller_snmp.

for wlc in wlclist:
      aplist.update(controller_snmp(wlc))

 

I usually create other functions above the main function, so add def controller_snmp(wlc): above main. In this functions we will create another empty aplist dictionary and create a dictionary called oidlist.

Dictionary

Dictionaries are unordered collections of key:value pairs. For this dictionary we are going to set the keys to the unique oids that we will walk and the values will be set to a string describing what we expect from the oid. We will also create a string with the snmp community string that is configured on the controller.

def controller_snmp(ip,host):
     aplist = {}
     oidlist = {'1.3.6.1.4.1.14179.2.2.1.1.33': 'apmac',
      '1.3.6.1.4.1.14179.2.2.1.1.30' : 'apgroup',
      '1.3.6.1.4.1.14179.2.2.1.1.3' : 'apname'}
     Snmpcommunity=’passw0rd’

Now were going to perform the snmpwalk on each of the oids. There is some error checking in here that I am not going to go into the details on.

for oid in oidlist:
              errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.bulkCmd(
                      cmdgen.CommunityData(snmpcommunity),
                      cmdgen.UdpTransportTarget((ip, 161)),0,25,
                      oid,
              )
              # Check for errors and print out results
              if errorIndication:
                      print(errorIndication)
              else:
                      if errorStatus:
                             print('%s at %s' % (
                                    errorStatus.prettyPrint(),
                                    errorIndex and varBinds[int(errorIndex)-1] or '?'
                                    )
                             )
                      else:
                             for varBindTableRow in varBindTable:
                                    for name, val in varBindTableRow:

ok now we are looking into a dictionary (varBindTableRow) where the key (name) is the full oid and the value (val) is the response.

If you are unfamiliar with SNMP walk you should play with it some, but for each AP you are going to have a line like this

1.3.6.1.4.1.14179.2.2.1.1.30.80.47.168.81.110.32 = STRING: “Building A”

As you can see the for this AP we have the full oid and the ap group that the AP is in. If you remove the oid we passed when we did the walk you are left with .80.47.168.81.110.32. This is the decimal value of the radio mac address of the AP.

Wait what? OK lets step through this, if you take each digit between the periods and convert them from decimal to hex you will end up with 50:2F:A8:51:6E:20. This is the base radio mac address of that AP.

So lets simplify this with python. First lets get just the portion of the oid for the radio mac address. We will take the “name” object and replace the oid we have defined(with an added period) with a blank. This will make the idx string object equal to the decimal value of the radio mac

idx = str(name).replace(oid+".", " ")

Now lets convert that to hex, since we will be doing this a lot we will use a separate function called idxToMac.

mac = idxToMac(idx)

This function will split the idx string by the periods into a list of the digits called ‘elements’, create an empty string ‘n’. Then iterate through the ‘elements’ converting to hex, removing the 0x in front, and making sure it formats to 2 hex characters. Then adds that to the string ‘n’. The function then returns the string ‘n’ back.

So, for our above example after running the string ‘mac’ would equal 502FA8516E20

def idxToMac(idx):
      elements = idx.split(".")
      count = 0
      n = ''
      for b in elements:
            count+=1
            octn = hex(int(b))
            mac = str(octn).replace("0x", "").zfill(2)
            n+= mac
      return n

We are now going to build our aplist dictionary. This dictionary is going to use the radio mac address of the APs as the key and the value will be….

Another dictionary.

This dictionary’s key is going to be the value of the oidlist dictionary that is currently being ran. For example if the apgroup oid is the one that is being walked then the key is going to be ‘apgroup’. The Value of this key is going to be the value that is returned from the oid.

So to do this we will check to see if the radio mac key is in the aplist dictionary, if not we will need to create the radio mac dictionary. Then we will set the key and value of the dictionary.

if mac in aplist:
      aplist[mac][oidlist[oid]]=val.prettyPrint()
else:
      aplist[mac]={}
      aplist[mac][oidlist[oid]]=val.prettyPrint()

All that’s left to do in controller_snmp function is to return the aplist. Make sure the indentation is correct. It should be just 1 intent in.

return aplist

Ok back to the main function. After we have gone through all the controllers we will create a dictionary “grouplist” were the key is the part of the AP name and the value is the AP group it should be in.

grouplist = {
            'BA':'Building A',
            'BB':'Bulding B'}

Now we will iterate through the APs and validate the ap groups.

for ap in aplist:
      for group in grouplist:
            if group in aplist[ap]['apname']:
                  if grouplist[group] not in aplist[ap]['apgroup']:
                        print("AP {0} is in {1} group and should be in {2} group".format(aplist[ap]['apname'],aplist[ap]['apgroup'],grouplist[group]))

Any AP that is found not to be in the correct group will be printed on the screen like the following.

AP APBA-001 is in Building B group and should be in Building A group

So putting this all together, if you’ve been following along you should have a script that looks like this.

Screen Shot 2019-05-29 at 3.04.25 PM

Getting Started with Python

Its been awhile since I’ve had a chance to update this page, but I wanted to continue with my “working faster” series. I’ve been using Python over the last few years to automate processes in my role as a Wireless network engineer. Some examples of things I have done with python are; configuring AP ports on switches; building AP config from a list of AP names and mac addresses; building controller config from a template and adding in variables; blasting config changes to multiple controllers; auditing AP config using SNMP and validating APs are named correctly, in the correct AP group based on name, in the correct flexconnect group, on the correct controller; and many other things. As mentioned I’ve used Python for SNMP but have also used if for entering CLI command, working with APIs and so much more.

As Python is a valuable tool to use as a network engineer, as it can speed up so many day to day tasks I figured it would be good to touch on it in this series. To get started I will walk through getting Python3 installed on a Mac and working with Sublime Text 3. If you haven’t dived into Python3 I hope to provide an easy transition so you can get your feet a little wet.

These are some great resources that I followed when installing Python3. So the information I provide below can also be found there.

Installing Python3 on mac – https://docs.python-guide.org/starting/install3/osx/

adding Python3 to Sublime – https://medium.com/@john.m.smalley/update-sublime-text-3-to-python-3-on-mac-ce57989bdbf3

Installing Python3

The first thing you will need to do is add GCC support. This is included in Xcode so if you already have that you can skip this part. If not install Command Line Tools. This does require an Apple account.

Once that is installed then you will need to install Homebrew. Open a terminal windows and paste the following line. This will go through what Homebrew will be changing and require you to accept the change before it starts.

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Once Homebrew is installed you will want to change were the Homebrew directory is installed. Paste the following command into

export PATH="/usr/local/opt/python/libexec/bin:$PATH"

Now that Python3 can be installed and in the right place, enter the following command to install Python3.

brew install python

Once that finishes installing you can validate that its installed by running this and it should return something like Python 3.7.2.

python3 --version

Adding Python3 to Sublime text 3

First thing you will need to location python3 is installed. Running this command will give you the location. Copy that.

$ which python3
/usr/local/bin/python3

Screen Shot 2019-03-11 at 5.31.59 PM

Then in Sublime select tools/Build System and select New Build System…

this will open a new window in sublime text. Replace the text in that window with this, making sure the path for Python3 is correct.

{
"cmd": ["/usr/local/bin/python3", "$file"]
, "selector": "source.python"
, "file_regex": "file \"(...*?)\", line ([0-9]+)"
}

Save that and name it Python3.sublime-build

Once you have that saved go back to Tools/Build System and you will see the newly create Python3 under Python. Select that. Open a new window and paste the following code.

import sys
print(sys.version)

Then press command+b to build it and a window on the bottom should pop up and display something like this. Confirming Python3 is working in Sublime.

3.7.2 (default, Feb 12 2019, 08:15:36)
[Clang 10.0.0 (clang-1000.11.45.5)]
[Finished in 0.1s]

Sublime Modules

As I mentioned the main modules I use are “Compare Side-by-Side”, “text pastry”, “select by regex”, and “Sort lines by selection”. Figured I should go into what they can do.

Compare Side-by-Side has a unique way of using it, which I will cover in a second, but to use the other ones you press shift+cmd+p to open the command palette and type in the name of the module. OR (the way I do it) create a short cut key to launch the module. To do this follow the instruction in my previous blog to edit the user key bindings, assign the keys you would like to use, and then add the command with the name of the function that shows in the command palette with _ in the place of spaces. like this { “keys”: [“ctrl+alt+a”], “command”: “select_by_regex_all”}. Once you save you key bindings your shortcut will show up in the command palette.

Compare Side-by-Side

This module will let you select two tabs that are open and will compare them with each other.

To do this right client on the Tab you want to compare the active tab with and select “Compare with active tab”. These 2 tabs will open a separate copy in a new window color coating all the differences from each other and will give you a brief count of the differences.Screen Shot 2018-04-05 at 10.10.56 PM.png

Unfortunately you can’t edit the files in this window, you will need to switch back to the original windows to edit in those tabs, but you can quickly jump through the differences on each of the files using shift+option+Up/Down. The bookmark function also works in this few so a lot of times I will bookmark the differences and copy all the bookmarked lines into a separate file.

Sort lines by Selection

As I mentioned last time you can make a column/vertical selection. With this module you can sort the lines by the column you have highlighted. This works just like sorting by a column in excel but can be done right in the text editor. This will also only sort the lines that have a selection.

Text Pastry

I use text pastry when I need to make a list of something numbered. If I needed to create a list of 200 APs I could type in AP- then open Text Pastry Command and put ” range 1 200 1 3″. Text Pastry would create 200 lines with AP-XXX (001-200).

 

When using text pastry “range” the first number is the starting number, the second is the ending number, the third is the increment number, and the forth is the number of digits for that number. You can also add “each=3” at the end and it will repeat each number 3 times.

select by regex

This is the module that I use the most, by far! If you are not familiar with regex, it is a super powerful tool, and being able to search text using regex will boost your productivity. I promise. Similar to text pastry, when using select by regex a command bar will pop up on the bottom. This is where you can put in the regular expresion you want to search for in the file.

here are some basic regex characters that you can use to build these searches:

\d – any number [0-9]
\w – any ASCII letter, digit, or _
\s – any white space
. – any character except line break
$ – end of line
\D, \W, and \S – one character that is not what the lowercase is. example \D is a character that is not a number.

There are also some regex characters for repeating characters:

+ – one or more (I use this to select to the end of the word)
{3} – three times
{2,4} – two to four times
{3,} – three or more times
* – zero or more times

Putting this together:

So lets say you’re looking at a log file and you want to select all the APs that are mentioned in the log. In this example APs are named like AP-SiteName-# (AP-Patio-001). typing AP-\w+-\d{3} in the regex command line, which will highlight all the AP names in the file. (Note the -‘s are actual -‘s in the AP name) Pressing enter will select all of them and allow you to copy/paste, change (by just typing), or by pressing right will add a curser after each instance of any AP name. More on this later.

Screen Shot 2018-04-23 at 10.45.43 PM.png

A powerful character combination with regex is ‘.*’ This allows you to select all characters before, after, or between searches. So lets say you want to select any line with an AP name in it. Using the same AP naming convention as above, just do .*AP-\w+-\d{3}.* Now the entire lines are selected. (Notice in the screen shot the lower lines off window on the right being selected as well) Screen Shot 2018-04-23 at 10.51.31 PM.png

Lets cover the .* “in between”. This means adding 2 different searches that the lines needs to contain to be a match. Remember that they search terms need to be in the correct order they will show up in the line as well. Using the same Log and AP naming format searching for .*Radio.*AP-\w+-\d{3}.* will select the radio events that I have in the log allowing me to pull those out and go through just those. Screen Shot 2018-04-23 at 10.58.23 PM.png

One of the other cool features of search by regex is that if you have a portion of the file selected it will only search within that selection. So if there is some text you want to change in just a portion of the file you can select that area and then use regex to make the selection and make the needed changes.

A lot of times I use search by regex to remove lines as well. For example if I’m searching for APs that are statically channeled and powered I will use .*AP-\w+-\d{3}.*\*.*\*.* (Note the \* searches for an actual * in the line) then once the lines are highlighted hit enter and then delete those lines. Now I have a list of APs that are statically set. Screen Shot 2018-04-23 at 11.15.28 PM.png

Regex is fun to play with and gives great joy when you are able to pull out the needed info quickly. Here are a couple other strings that I use frequently to give more examples of things you can do.

.*\d*[13579]$ – selects all lines that end in an odd number. I use this when I need to upgrade or reboot APs and want to stagger the reboot some. With a list of APs I can quickly select them all, press shift+cmd+l to add a cursor to each line, hit left to go to the beginning of the AP names and type the command to reload the AP building all the lines I need to reload the APs. Then using this search I can select the odd APs, cut them and then paste into the controller leaving just the evens in the text file. You can get more granular with this if you need. For example if you wanted to select the evens in the 20’s at a specific site you might do the following. – .*AP-Patio-02.*\d*[02468]$

((.*\n){1,5}) – this puts a cursor on every 5th line! Just make sure to press left to deselect all the lines. Obviously changing the 5 to a different number will change the number of lines.

\s$ – selects all the white space at the end of lines. Nice for clean up!

 

Well there you go. A quick dive into the Modules that I use frequently. Thanks for reading.

Sublime – Macros and key binding

Macros

Macros are one of those things that I typically forget to use. Its something that I used to use with Notepad++ a ton and when I first switched to Sublime I used them frequently. But as I got used to using the multiline editing that I wrote about last time I forget about macros some. I do have some saved that I still use when the times comes though.

Macros gives you the ability to record what you type and how you move the cursor and allows you to play that back on multiple lines. Lets say for example, you have a list of 50 APs that you need to move to another controller. Put you cursor at the beginning of the line of the first AP and under tools select ‘record macro’ or press ctrl+Q, type the needed config and then move the cursor to the beginning of the next line. The select ‘Stop recording macro’ or press ctrl+Q again. Now you can press shift+ctrl+Q to playback the macro for each line.

Screen Shot 2018-04-10 at 9.58.32 PM.png

If you have a macro that you use frequently, after recording it you can save the macro then under tools/macros/users it will list all your saved macros allowing for easy playback.

Don’t forget that you can use the multiline editing to put a cursor at the beginning of every line and then playback a macro to make the changes to all lines at once!

These saved macros are located in /Library/Application Support/Sublime Text 3/Packages.

Key Binding

Sublime text allows you to open and edit the file will all of the shortcut keys and also add user defined shortcut keys. I would recommend against editing the default list but if you want to assign a different set of keys its fairly easy to do. To open this file, under Sublime Text/ Preferences select ‘key bindings’. This will open a new window with a 2 columns layout. On the left you will have the list of Default key bindings and on the right any User defined key bindings that you have added.

To add a key binding, inside of the []s add a line like the following:

{“keys”: [“shift+ctrl+alt+p”], “command”: “run_macro_file”, “args”: {“file”: “res://Packages/User/primary-base.sublime-macro”}}

this will create a shortcut key to run the macro I saved as primary-base.

Screen Shot 2018-04-10 at 10.07.03 PM.png

Sublime Text 3 – a network engineers best friend

Sublime text is my go to app. In my opinion it can be a network engineers best friend. I use this program for all kinds of text editing: taking notes, building configs, validation of changes, scripting, text sorting, scrubbing logs, and so much more. It is always open and I’m almost always copying, manipulating, and pasting in and out of it.

Sublime text 3 can be downloaded here https://www.sublimetext.com/3 and can be tried for free. There is a cost of $80 to continue using after the trial period. Some of the guys I work with use Atom which I believe is free, but I like to argue with them that I can run circles around them with Sublime 🙂 not sure if that is true…

Installing Modules

One of the features I like about Sublime is installing modules. Some of the modules I use are “Compare Side-by-Side”, “text pastry”, “select by regex”, and “Sort lines by selection”.Well get into these a little later but to install modules you need to install Package control. This page https://packagecontrol.io/installation shows how to do that. Once Package control is installed you can press shift+cmd+p to open the command palette type in “install package”, and select “Command Package: Install Package”. Screen Shot 2018-03-12 at 10.29.11 PM.pngThis will open a new window where you can type in the name of the module you want installed and select it for installation.Screen Shot 2018-03-12 at 9.09.53 PM.png

For now, we will cover some of the things that are native to sublime text and get into the modules later.

Multiline editing

One of the main things I love about Sublime is being able to set the cursor to every line of a selection, allowing you to type on every line at once. For example, lets say you have a list of APs and you need to move them to a different controller.

Screen Shot 2018-03-12 at 10.00.31 PM.pngtyping on multiple lines at once

Select the list of APs, press shift+cmd+l and bam, you have a cursor at the end of each line. Then cmd+← to take the cursors to the beginning of each line and type in “config ap primary-base controller2” move the cursor to the end of the lines and add the IP address. Done.

Changing case of text

Or lets say you have a list of Mac addresses and they are in the wrong format… Select them, shift+cmd+l and then you can easily add :’s, -’s, or .’s where needed. Now lets say these Mac addresses are all CAPs and you want them lower case. Select them all (cmd+a) then press cmd+k then cmd+l. This will make all letters in the selection lower case. You can make the letters in the section capitalized by pressing cmd+k then cmd+u.

Permute Unique Lines

Now lets say you have a list of APs  that consists of duplicates and you want just a list of unique APs. Under edit on the menu bar select Permute lines/ Unique.Screen Shot 2018-03-12 at 9.35.14 PM.png

Column/vertical selection

One of the other cool features of Sublime is being able to make a selection “column” in a text file. Lets say you have an output of a controller and you want to select all the Mac addresses that show up in the list. By holding the option key you can click and drag a selection box selecting just the text inside of the area you aqdragged. Screen Shot 2018-03-12 at 9.49.51 PM.png

Bookmarks

Screen Shot 2018-03-12 at 10.06.26 PM.png

Using bookmarks you can make reviewing and comparing files a little easier. To bookmark a line press cmd+F2, this will add a little flag to the left of the line. This is helpful when the file is thousands of lines long and you know you want to go back to change something. Pressing F2 will move the cursor to the next bookmark and shift+F2 will go to the previous bookmark. You can also select all bookmarked lines to copy and paste them.

Multifile search

If you have multiple files open in Sublime you can run a Find or Find and Replace across

Screen Shot 2018-03-12 at 10.14.45 PM.png

the files that are open. Shift+Cmd+f will bring up a box on the bottom where you can search across all files that are open. You can also add a folder in the “where” section. Select the the icon to the right on the “where” section and click add folder. Once you have a folder selection and the find filled in (and replace if wanted) Sublime will do all the heavy lifting searching for the string in all the files and just open a new text file listing any files in that folder that have a match and lists out the surrounding lines of the match

Screen Shot 2018-03-12 at 10.24.28 PM.pngScreen Shot 2018-03-12 at 10.23.25 PM.png

There’s just so much to this Program. I will cover some more native features and get into the modules some next time.

Thanks for reading.

Ekahau – Toggling Views

Two weeks ago I was at WLPC in Phoenix. This was my 2nd year going to WLPC and it was awesome! I’ve been fortunate enough to attend a boot camp prior to the conference each year. Last year I took Peter’s CWAP class. I highly recommend this class if you haven’t taken it. Even if you already have your CWAP cert it would be well worth it! This year I took the ECSE class. Ferney was great! He kept the information flowing and we had a fun time.

I’ve been using Ekahau for a little bit over a year. Definitely not an expert by any means, and not a daily user, but have used it pretty frequently.

Screen Shot 2018-03-05 at 9.49.18 PM.png

While sitting in class I was looking at the list of shortcut keys and decided to write a short list of things that I wish were on there. Some of the things that I had on my list were toggling the different options in the view menu. When reviewing a survey I tend to hid the walk paths right after giving them a quick glance, when designing I tend to hide the walls after they are all in place, and before building reports from a survey I usually hide the APs. So after dinner one night I decided to see if I could add some TouchBar toggles for these using applescripts and BetterTouchTools.

I figured I would share these applescripts so others can use these too, or tweak them to work for some of the other view options.

So in BetterTouchTools you can set TouchBar shortcuts (or even keyboard shortcuts if you don’t have a TouchBar) that will only work if you are in a specific app. In my last blog  post I wrote about how to create the TouchBar for specific apps and how to have an AppleScript run as an action so head back there if needed.

btt - applescript.png

Once you have this window open  you can enter the applescript you want to run with the trigger. You can even test right from this window by selecting the “Run Script” button

Screen Shot 2018-03-05 at 5.44.37 PM

For the most part, you can use this and just change the menu item you want to toggle. This example would be used to toggle the “Survey” option in the view list.

activate application "Ekahau Site Survey"
tell application "System Events"
	tell process "Ekahau Site Survey"
		tell menu bar 1
			click menu item "Surveys" of menu "View" of menu bar item "View"
		end tell
	end tell
end tell

by simply changing the one line to following you can use the script to toggle the Walls

click menu item "Walls" of menu "View" of menu bar item "View"

Something to remember is that the sub options require you to put 6 spaces before the name. Like this one that toggles the AP names.

click menu item "      Names" of menu "View" of menu bar item "View" 

If a view option has sub sections, when you click on it will automatically check (or uncheck) all the sub sections. For example, when turning Access Points back on most of the time I go back and de select the Names. So I wanted to automate that process with the applescript toggle. So came up with the following and it works super fast!

activate application "Ekahau Site Survey"
tell application "System Events"
	tell process "Ekahau Site Survey"
		tell menu bar 1
			click menu item "Access Points" of menu "View" of menu bar item "View"
			set APsOn to (value of attribute "AXMenuItemMarkChar" of menu item "Access Points" of menu "View" of menu bar item "View") is "✓"
			if APsOn then
				set NamesOn to (value of attribute "AXMenuItemMarkChar" of menu item "      Names" of menu "View" of menu bar item "View") is "✓"
				if NamesOn then
					click menu item "      Names" of menu "View" of menu bar item "View"
				end if
			end if
			
		end tell
	end tell
end tell

Walking through this with you, the script toggles “Access Points” then checks if there is a ✓ by it. If there is a ✓ then it checks if Names has a ✓ by it. If so it will toggle Names so they don’t show.

Playing around I have also added toggles for refreshing the visualization.

activate application "Ekahau Site Survey"
tell application "System Events"
	tell process "Ekahau Site Survey"
		click checkbox 1 of window 1
	end tell
end tell

and toggling the Frequency monitor overlay

activate application "Ekahau Site Survey"
tell application "System Events"
	tell process "Ekahau Site Survey"
		tell window 1
			click button 7
		end tell
	end tell
end tell

If you are like me and like to use shortcuts hopefully you find these useful and can incorporate them.