From 95fddf48c39eea078828926bfa55f9557fa484f5 Mon Sep 17 00:00:00 2001 From: Joe Bennett Date: Fri, 4 May 2018 07:44:05 +1000 Subject: [PATCH 1/5] Added new script to add static routes from YAML file --- AddStaticRoute.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 AddStaticRoute.py diff --git a/AddStaticRoute.py b/AddStaticRoute.py new file mode 100644 index 0000000..b6e3ec9 --- /dev/null +++ b/AddStaticRoute.py @@ -0,0 +1,37 @@ +# Test YML looks like: +# --- +# NetworkID: Z_9512345678 +# API_Key: c1234567890abcdef01234 +# Gateway: +# - GW: "1.2.3.4" +# Networks: +# - Network: "10.1.1.0/24" +# Name: "Net 10.1.1.0-24" +# - Network: "10.1.2.0/24" +# Name: "Net 10.1.2.0-24" +# - GW: "2.3.4.5" +# Networks: +# - Network: "10.2.1.0/24" +# Name: "10.2.1.0-24" +# - Network: "10.2.2.0/24" +# Name: "10.2.2.0-24" +import yaml, requests, json +with open ("StaticRoutes.yml") as file1: + config = yaml.load (file1) +url = "https://dashboard.meraki.com/api/v0/networks/%s/staticRoutes" % config["NetworkID"] +headers = { + 'X-Cisco-Meraki-API-Key': "%s" %config["API_Key"], + 'Content-Type': "application/json", + 'Cache-Control': "no-cache", + } +for gateway in config["Gateway"]: + GatewayIP=gateway["GW"] + for network in gateway["Networks"]: + payload = { + "name" : network["Name"], + "subnet": network["Network"], + "gatewayIp": gateway["GW"], + "enabled":"true" + } + response = requests.request("POST", url, data=json.dumps(payload), headers=headers) + print(response.status_code, response.text) \ No newline at end of file From 7838280f152983cf041a86d1850197b663833e3a Mon Sep 17 00:00:00 2001 From: Joe Bennett Date: Fri, 4 May 2018 07:52:01 +1000 Subject: [PATCH 2/5] Updated Readme.MD with new script --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7974ea8..f7e0520 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Files contained in this repository: **Installing Python on Windows.txt:** General info for installing Python 3 on Windows +**AddStaticRoute.py:** This simple script reads a list of static routes from a YAML file and adds them to a Network. + **copymxvlans.py:** This script can be used to export MX VLAN configuration of a source org to a file and import it to a destination org. The script will look for the exact same network names as they were in the source org. Use copynetworks.py and movedevices.py to migrate networks and devices if needed. **copynetworks.py:** Copies networks and their base attributes from one organization to another. Does not move devices over or copy individual device configuration. Combined networks will be copied as "wireless switch appliance". From f3d698ef22285f4ba9961ae5ccb47086bdcb0f6c Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 31 May 2018 18:43:39 +1000 Subject: [PATCH 3/5] Added meraki-export.py Dump an org's config to a YAML file --- meraki-export.py | 124 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100755 meraki-export.py diff --git a/meraki-export.py b/meraki-export.py new file mode 100755 index 0000000..47b8ccb --- /dev/null +++ b/meraki-export.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# +# Before running this script you need to install these two modules: +# pip install requests +# pip install meraki +# +from meraki import meraki +import argparse +import yaml + +config={} +config["Organization"]={} +config["Organization"]["Network"]={} +def get_org_id(apikey,orgName,suppressprint): + result = meraki.myorgaccess(apikey, suppressprint) + for row in result: + if row['name'] == orgName: + return row['id'] + + raise ValueError('The organization name does not exist') + +def admins(apikey, orgid, suppressprint): + myOrgAdmins=meraki.getorgadmins(apikey, orgid, suppressprint) + config["Organization"]["Admins"]=myOrgAdmins + print("Got admins") + +def mx_l3_fw_rules(apikey,networkid,suppressprint): + myRules=meraki.getmxl3fwrules(apikey, networkid, suppressprint)[0:-1] + network["L3-Firewall-Rules"]=myRules + print("Got Layer 3 firewall rules") + +def mx_cellular_fw_rules(apikey,networkid,suppressprint): + myRules=meraki.getmxcellularfwrules(apikey, networkid, suppressprint)[0:-1] + network["Cell-Firewall-Rules"]=myRules + print("Got mobile firewall rules") + +def mx_vpn_fw_rules(apikey,orgid,suppressprint): + myRules=meraki.getmxvpnfwrules(apikey, orgid, suppressprint)[0:-1] + config["Organization"]["MX-VPN-Firewall-Rules"]=myRules + print("Got VPN firewall rules") + +def vpn_settings(apikey,networkid,suppressprint): + myVPN=meraki.getvpnsettings(apikey, networkid, suppressprint) + network["VPN-Settings"]=myVPN + print("Got VPN settings") + +def snmp_settings(apikey,orgid,suppressprint): + mySNMP=meraki.getsnmpsettings(apikey, orgid, suppressprint) + if 'v2CommunityString' in mySNMP: + del mySNMP['v2CommunityString'] + if 'hostname' in mySNMP: + del mySNMP['hostname'] + if 'port' in mySNMP: + del mySNMP['port'] + config["Organization"]["SNMP"]=mySNMP + print("Got SNMP settings") + +def non_meraki_vpn_peers(apikey,orgid,suppressprint): + myPeers=meraki.getnonmerakivpnpeers(apikey,orgid,suppressprint) + config["Organization"]["Non-Meraki-VPN"]=myPeers + print("Got non-Meraki VPN peers") + +def static_routes(apikey,networkid,suppressprint): + myRoutes=meraki.getstaticroutes(apikey,networkid,suppressprint) + if myRoutes is None: + return + network["Routes"]=myRoutes + print("Got static routes") + +def ssid_settings(apikey,networkid,suppressprint): + mySSIDs=meraki.getssids(apikey, networkid, suppressprint) + if mySSIDs is None: + return + for row in mySSIDs: + myRules=meraki.getssidl3fwrules(apikey, networkid, row['number'], suppressprint)[0:-2] + row["rules"]=myRules + network["SSID"][row["number"]]=row + print("Got SSID "+str(row["number"])) +#################################################### +# Main program +# ################################################## +parser = argparse.ArgumentParser(description='Backup a Meraki config to an offline file.') +parser.add_argument('-v', help='Enable verbose mode',action='store_true') +parser.add_argument('apiKey', help='The API Key') +parser.add_argument('orgName', help='The name of a Meraki organisation') +args = parser.parse_args() + +suppressprint=True +if args.v: + suppressprint=False + +apikey=args.apiKey +orgid=get_org_id(apikey,args.orgName,suppressprint) +file="%s.yml"%args.orgName +config["Organization"]["Name"]=args.orgName +config["Organization"]["ID"]=orgid +admins(apikey, orgid, suppressprint) +mx_vpn_fw_rules(apikey,orgid,suppressprint) +snmp_settings(apikey,orgid,suppressprint) +non_meraki_vpn_peers(apikey,orgid,suppressprint) +myNetworks = meraki.getnetworklist(apikey, orgid, None, suppressprint) +for row in myNetworks: + tags=row['tags'] + if tags == None: + tags = "" + networkType=row['type'] + if networkType == 'combined': + networkType = 'wireless switch appliance phone' + if networkType == 'systems manager': + continue + print("Processing network "+row['name']) + network={"name": row["name"], "networkType": networkType, "tags": tags, "timeZone":row["timeZone"]} + mx_cellular_fw_rules(apikey,row['id'],suppressprint) + mx_l3_fw_rules(apikey,row['id'],suppressprint) + vpn_settings(apikey,row['id'],suppressprint) + static_routes(apikey,row["id"],suppressprint) + network["SSID"]={} + ssid_settings(apikey,row['id'],suppressprint) + config["Organization"]["Network"][row["id"]]=network +with open(file, 'w') as file: + file.write("---\n") + file.write(yaml.dump(config)) + file.flush() + file.close() From 760dada57dd40c48f5bd1e96ee74fadc54aad5b2 Mon Sep 17 00:00:00 2001 From: Joe Bennett Date: Thu, 31 May 2018 21:01:16 +1000 Subject: [PATCH 4/5] Changed SSIDs and networks from dicts to lists to better reflect layouts --- meraki-export.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/meraki-export.py b/meraki-export.py index 47b8ccb..2bb8555 100755 --- a/meraki-export.py +++ b/meraki-export.py @@ -10,7 +10,7 @@ config={} config["Organization"]={} -config["Organization"]["Network"]={} +config["Organization"]["Network"]=[] def get_org_id(apikey,orgName,suppressprint): result = meraki.myorgaccess(apikey, suppressprint) for row in result: @@ -46,12 +46,6 @@ def vpn_settings(apikey,networkid,suppressprint): def snmp_settings(apikey,orgid,suppressprint): mySNMP=meraki.getsnmpsettings(apikey, orgid, suppressprint) - if 'v2CommunityString' in mySNMP: - del mySNMP['v2CommunityString'] - if 'hostname' in mySNMP: - del mySNMP['hostname'] - if 'port' in mySNMP: - del mySNMP['port'] config["Organization"]["SNMP"]=mySNMP print("Got SNMP settings") @@ -74,7 +68,7 @@ def ssid_settings(apikey,networkid,suppressprint): for row in mySSIDs: myRules=meraki.getssidl3fwrules(apikey, networkid, row['number'], suppressprint)[0:-2] row["rules"]=myRules - network["SSID"][row["number"]]=row + network["SSID"].append(row) print("Got SSID "+str(row["number"])) #################################################### # Main program @@ -114,9 +108,9 @@ def ssid_settings(apikey,networkid,suppressprint): mx_l3_fw_rules(apikey,row['id'],suppressprint) vpn_settings(apikey,row['id'],suppressprint) static_routes(apikey,row["id"],suppressprint) - network["SSID"]={} + network["SSID"]=[] ssid_settings(apikey,row['id'],suppressprint) - config["Organization"]["Network"][row["id"]]=network + config["Organization"]["Network"].append(network) with open(file, 'w') as file: file.write("---\n") file.write(yaml.dump(config)) From ad5890f0e88e0571c08040fe2b70033a44322aeb Mon Sep 17 00:00:00 2001 From: Joe Bennett Date: Fri, 1 Jun 2018 11:43:14 +1000 Subject: [PATCH 5/5] Added comments and tidied --- meraki-export.py | 51 +++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/meraki-export.py b/meraki-export.py index 2bb8555..2e7c3b0 100755 --- a/meraki-export.py +++ b/meraki-export.py @@ -1,17 +1,14 @@ #!/usr/bin/env python3 -# -# Before running this script you need to install these two modules: -# pip install requests -# pip install meraki -# +# Before running this script you need to install these three modules: +# pip install meraki yaml argparse from meraki import meraki import argparse import yaml -config={} -config["Organization"]={} -config["Organization"]["Network"]=[] -def get_org_id(apikey,orgName,suppressprint): +config={} #Define config as a dictionary +config["Organization"]={} #Define organization as a dictionary +config["Organization"]["Network"]=[] #but Network is a list +def get_org_id(apikey,orgName,suppressprint): #Turns an org name into an org ID result = meraki.myorgaccess(apikey, suppressprint) for row in result: if row['name'] == orgName: @@ -19,49 +16,49 @@ def get_org_id(apikey,orgName,suppressprint): raise ValueError('The organization name does not exist') -def admins(apikey, orgid, suppressprint): +def admins(apikey, orgid, suppressprint): # Retrieves a list of admins myOrgAdmins=meraki.getorgadmins(apikey, orgid, suppressprint) config["Organization"]["Admins"]=myOrgAdmins print("Got admins") -def mx_l3_fw_rules(apikey,networkid,suppressprint): +def mx_l3_fw_rules(apikey,networkid,suppressprint): #Retrieves the Layer 3 firewall rules on the MX myRules=meraki.getmxl3fwrules(apikey, networkid, suppressprint)[0:-1] network["L3-Firewall-Rules"]=myRules print("Got Layer 3 firewall rules") -def mx_cellular_fw_rules(apikey,networkid,suppressprint): +def mx_cellular_fw_rules(apikey,networkid,suppressprint): # Retrieves the mobile firewall rules myRules=meraki.getmxcellularfwrules(apikey, networkid, suppressprint)[0:-1] network["Cell-Firewall-Rules"]=myRules print("Got mobile firewall rules") -def mx_vpn_fw_rules(apikey,orgid,suppressprint): +def mx_vpn_fw_rules(apikey,orgid,suppressprint): # Retrieves the VPN firewall rules myRules=meraki.getmxvpnfwrules(apikey, orgid, suppressprint)[0:-1] config["Organization"]["MX-VPN-Firewall-Rules"]=myRules print("Got VPN firewall rules") -def vpn_settings(apikey,networkid,suppressprint): +def vpn_settings(apikey,networkid,suppressprint): # Retrieves Meraki S2S VPN settings myVPN=meraki.getvpnsettings(apikey, networkid, suppressprint) network["VPN-Settings"]=myVPN print("Got VPN settings") -def snmp_settings(apikey,orgid,suppressprint): +def snmp_settings(apikey,orgid,suppressprint): # Retrieves SNMP settings mySNMP=meraki.getsnmpsettings(apikey, orgid, suppressprint) config["Organization"]["SNMP"]=mySNMP print("Got SNMP settings") -def non_meraki_vpn_peers(apikey,orgid,suppressprint): +def non_meraki_vpn_peers(apikey,orgid,suppressprint): # Retrieves non-Meraki site-to-site myPeers=meraki.getnonmerakivpnpeers(apikey,orgid,suppressprint) config["Organization"]["Non-Meraki-VPN"]=myPeers print("Got non-Meraki VPN peers") -def static_routes(apikey,networkid,suppressprint): +def static_routes(apikey,networkid,suppressprint): # Retrieve any static routes myRoutes=meraki.getstaticroutes(apikey,networkid,suppressprint) if myRoutes is None: return network["Routes"]=myRoutes print("Got static routes") -def ssid_settings(apikey,networkid,suppressprint): +def ssid_settings(apikey,networkid,suppressprint): # Retrieve all SSID settings mySSIDs=meraki.getssids(apikey, networkid, suppressprint) if mySSIDs is None: return @@ -77,15 +74,15 @@ def ssid_settings(apikey,networkid,suppressprint): parser.add_argument('-v', help='Enable verbose mode',action='store_true') parser.add_argument('apiKey', help='The API Key') parser.add_argument('orgName', help='The name of a Meraki organisation') -args = parser.parse_args() +args = parser.parse_args() # Defines arguments passed on the command line when running program suppressprint=True if args.v: suppressprint=False apikey=args.apiKey -orgid=get_org_id(apikey,args.orgName,suppressprint) -file="%s.yml"%args.orgName +orgid=get_org_id(apikey,args.orgName,suppressprint) +file="%s.yml"%args.orgName # set the filename from the network name config["Organization"]["Name"]=args.orgName config["Organization"]["ID"]=orgid admins(apikey, orgid, suppressprint) @@ -93,14 +90,14 @@ def ssid_settings(apikey,networkid,suppressprint): snmp_settings(apikey,orgid,suppressprint) non_meraki_vpn_peers(apikey,orgid,suppressprint) myNetworks = meraki.getnetworklist(apikey, orgid, None, suppressprint) -for row in myNetworks: +for row in myNetworks: # Iterate through the networks in the org tags=row['tags'] if tags == None: tags = "" networkType=row['type'] - if networkType == 'combined': + if networkType == 'combined': # Combined is not valid to upload! networkType = 'wireless switch appliance phone' - if networkType == 'systems manager': + if networkType == 'systems manager': # We don't care about MDM networks continue print("Processing network "+row['name']) network={"name": row["name"], "networkType": networkType, "tags": tags, "timeZone":row["timeZone"]} @@ -111,8 +108,8 @@ def ssid_settings(apikey,networkid,suppressprint): network["SSID"]=[] ssid_settings(apikey,row['id'],suppressprint) config["Organization"]["Network"].append(network) -with open(file, 'w') as file: +with open(file, 'w') as file: # open the file we defined earlier to write to file.write("---\n") - file.write(yaml.dump(config)) + file.write(yaml.dump(config)) # convert the config dict to yaml and save file.flush() - file.close() + file.close() \ No newline at end of file