Skip to content
Daniel Bostock's Blog

Welcome back!

Hey everyone,

Here’s my next dev diary going over a new feature I have added into the application. Previously the devices in the sites where just using their default names which is their MAC address. The next feature to be added to the application was device naming. I thought it would be relatively simple, but due to my lack of experience and knowledge with Python doing this was not so simple.

I can safely say, this has been yet another great learning experience that I will take forward with me in my future usage with Python.

GitHub Repo: https://github.com/danielbostock/meraki_site_creator

Summary of Features

If you missed my first dev diary post and have not seen what I have been doing, see a summary list below of all the current features.

  • Claim devices – CSV, manual input order Meraki order number
  • Create a name for a site based on pre-determined naming convention
  • Create a Meraki network, name the network and add devices into the network
  • Name all devices in the network according to the networks name and number of the types of devices at the site – ie: Switch 2 = …SITE1-SW2
  • Bind the network and it’s respective devices to a configuration template

Device naming is important, and very helpful and saves everyone a lot of time when it is done consistently the same every time. I will now explore this a little bit then talk about where the application is at and where I will be taking it in the next release.

device_namer

This is the new class that I have introduced. With the addition of this code, it now gets the application to a level that even if I used it tomorrow to make a site, I could confidently save myself about 20mins of admin. This time saving increases exponentially over time of course which is the beauty of pushing network deployment tasks like this to code.

It took some time of messing around and researching what the best way might be for this part of the application. In the end, I settled on pushing it to a MVP. I had to effectively compromise on a dynamic way to verify the model of the hardware when it came to naming.

If this doesn’t make sense, effectively, when we label devices we know whether they are an Access Point, Switch etc. However code doesn’t immediately know this. Therefore I needed a way to iterate over a list of serial number and then determine what model it was so that I could make a name that was something like SITE1-SW1 or SITE1-AP1.

The Meraki API Call

After investigating the Meraki API’s, the following API call seemed like the most reasonable one to use.

get-devicehttps://developer.cisco.com/meraki/api-v1/#!get-device

This API call requires only a serial number, the data return is JSON which is great because I then can read that data as a dictionary and make a if statement that will effectively filter results into their respective models.

Here is a copy of the structured API response data in JSON from this API call.

{
    "lat": 37.4180951010362,
    "lng": -122.098531723022,
    "address": "",
    "serial": "Never-Gonna",
    "mac": "Give-You-Up",
    "wan1Ip": null,
    "wan2Ip": null,
    "url": "https://n335.meraki.com/FF-AUS-LOGAN-app/n/zClkcapf/manage/nodes/new_list/189218145883345",
    "networkId": "Never-Gonna",
    "tags": [],
    "name": "Let-You-Down",
    "model": "MX67C-WW",
    "firmware": "Not running configured version",
    "floorPlanId": null
}

With this data returned, I was looking for a simple way to sort an access point from a switch. The obvious one was “key”: “model”; “value”:”MX67C-WW”. However this as I will soon explain this is problematic long term.

What I actually thought might be a good way is leveraging key: “firmware”, value: “…”. In this output you can see that the firmware is empty, or rather prefilled with a generic message. This message is actually because the device is not powered on. Since the API call is doing an active query on your device, this data is pulled live, which is great but in this situation not so much.

The returned data below is from a running different MX. As you can see here the model is different but it has data in the firmware key – “wired-14-53“.

{
    "lat": -27.47547,
    "lng": 153.01011,
    "address": "176 Montague Road, South Brisbane, Queensland, Australia, 4101",
    "serial": "Never-Gonna",
    "mac": "Give-You-Up",
    "wan1Ip": null,
    "wan2Ip": "Never-Gonna",
    "tags": [
        "recently-added"
    ],
    "url": "https://n335.meraki.com/Silver_Tier2-app/n/eMfXWapf/manage/nodes/new_list/48649292842939",
    "networkId": "Let-You-Down",
    "model": "MX68CW-WW",
    "firmware": "wired-14-53",
    "floorPlanId": null
}

I initially thought I could use this as a way of iterating through the model types, as the firmware when it is for an AP is called wireless-xx-xx, and switch has it’s own one as well. I could have used this as a simple way to detect which type of device it is an appropriately apply the right type in the name.

However the device first needs to be powered on and connected to the dashboard for this info to be obtained. So this ruled this option out. I had to do it the not so nice way initially to get it working. Because of this, in order to get the function to a working state, I wrote the following.

                if get_type in ('MR36'):
                    ap.append(serial)
                    for number, serial in enumerate(ap, start=1):
                        name = (self.site + '-AP')
                        self.update(name,serial,number)
                elif get_type in ('MS120-24P'):
                    sw.append(serial)
                    for number, serial in enumerate(sw, start=1):
                        name = (self.site + '-SW')
                        self.update(name,serial,number)
                elif get_type in ('MX67C-WW'):
                    mx.append(serial)
                    print(mx)
                    for number, serial in enumerate(mx, start=1):
                        name = (self.site + '-MX')
                        self.update(name,serial,number)

I knew this wasn’t the best way to solve the issue with Python, and would be very hard to maintain. Now I must re-iterate that I am very much a novice when it comes to working with Python and at times, especially like this I don’t have the background knowledge of all the choices I have available here. Anyway, I presumed there might be a way in which I could maybe do some sort of if statement which searched the ‘model’ value string for the first two letters. In the world of Meraki, this is pretty easy and consistent so this was a simple way.

str.startswith()

This was my solution to this little problem. After 10mins of reading and another 10 of testing, I ended up with the following updated code.

    def name(self):
        complete = False
        #print(serials)
        ap = []
        sw = []
        mx = []
        while complete == False:
            for serial in (self.serials):
                get_device = self.dashboard.devices.getDevice(serial=(serial))
                get_type = get_device['model']
                if get_type.startswith("MR"):
                    ap.append(serial)
                    for number, serial in enumerate(ap, start=1):
                        name = (self.site + '-AP')
                        self.update(name,serial,number)
                elif get_type.startswith("MS"):
                    sw.append(serial)
                    for number, serial in enumerate(sw, start=1):
                        name = (self.site + '-SW')
                        self.update(name,serial,number)
                elif get_type.startswith("MX"):
                    mx.append(serial)
                    for number, serial in enumerate(mx, start=1):
                        name = (self.site + '-MX')
                        self.update(name,serial,number)

This code now will look for those respective first two characters, which obviously means that if we suddenly have MR56, or older model like MR42, the code can handle this and it is not dependent on me using a specific string. Long term, I will actually make this a var in a configuration file. If Meraki come up with the great idea to start the naming of their access points after the famous singer Rick Astley and the new model name is something like – RA36. I can access the configuration file and only have to update it there, as I expect to use this reference in other code specifically when I email information back to user of the application.

Looping & Counting

Now that I had a condition to work with, I actually had to have a way to increment the number in the name that I would be appending for the respective device. Initially I was using a typical for loop. The code initially looked like this.

    def get_devices(self):
        completed = False
        while completed == False:
            updated = False
            while updated == False:
                for serial in serials:
                    get_device = self.dashboard.devices.getDevice(serial=(serial))
                    get_type = get_device['model']
                    name = (self.site + 'AP')
                    device_number = 0
                    if get_type in ('MR36'):  
                        device_named = False
                        while device_named == False:
                            if device_number <= 0:
                                for model in serials:
                                    print (serials)
                                    device_number += 1
                                    number = str(device_number)
                                    update_device = self.dashboard.devices.updateDevice(serial=(serial), name=(name + number))
                                    device_number += 1
                                    ('current number' + device_number)
                                    serials.remove(serial)
                            elif serials == []:
                                print('Naming Complete')
                                device_named = True
                                return device_named
                            else:
                                print('Hello There')
                                break

If you understand Python, you will know why exactly why this wasn’t actually going to properly increment my device name properly. Effectively when I ran the code this way, it labelled all my devices “…-AP1”. Which is not at all what I was after. What took me the longest out of writing this next update was how to effectively have some sort of counter that I reference for the device naming so if I had 6 APs, they would be consecutively numbered. The code also had to detect the number as each time this application is run the number could ultimately vary.

Eventually after much frustration of trying to work out some complex looping, I realised surely someone else has wanted to count inside a loop before.

enumerate()

This built-in python function was the answer. This did it perfectly, well maybe there is a better way, but at the time of writing this, this is the best way I have found. So from what existed above, I was able to remove quite a few lines of code and make it much easier to read and manage, plus also work!

                    for number, serial in enumerate(ap, start=1):
                        name = (self.site + '-AP')
                        self.update(name,serial,number)

What’s Next?

Just adding this one feature, I have further developed my understanding and usage of classes and respective methods. I have the further learned the power of startswith() and enumerate() built-in Python functions!

The next step from here is to actually make the region selection and brand selection more dynamic. Right now they are very static and not very malleable. Not sure how long this is going to take me as there are quite a few things to learn with this next feature – but I am looking forward to it!

Leave a Reply

Your email address will not be published. Required fields are marked *