From 5e98ce0ed170b111d3f4e73ecf4cb1e780dbcb07 Mon Sep 17 00:00:00 2001 From: "John M. Kuchta" Date: Mon, 13 Nov 2023 18:36:30 -0800 Subject: [PATCH 1/4] Create local_subnet_dumper.py New example script for IPAM. --- examples/local_subnet_dumper.py | 136 ++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 examples/local_subnet_dumper.py diff --git a/examples/local_subnet_dumper.py b/examples/local_subnet_dumper.py new file mode 100644 index 00000000..cfca5a0b --- /dev/null +++ b/examples/local_subnet_dumper.py @@ -0,0 +1,136 @@ +import json + +import meraki + +""" +2023-11-13 +Author: John M. Kuchta ( TKIPisalegacycipher // https://github.com/TKIPisalegacycipher ) +Requrires: Meraki library v1.39.0 or later. + +This script gathers all your appliances' local subnets from your organizations' networks and dumps them to a JSON file. +You might find this handy for certain IPAM exercises. Subnets from appliances in "single LAN" mode will have VLAN 0. + +EXTRA CREDIT +If you are feeling adventurous, or just want an opportunity to flex your Python skills, consider re-writing this script +to work asynchronously, which can substantially improve the speed for large environments. You might start by gathering +all the relevant information asynchronously, then doing the list comprehensions after the API calls are complete. +""" + +# You can exclude specific organization or networks here. This is optional but recommended if you have a large +# deployment including lots of irrelevant networks. +excluded_org_ids = [] +excluded_network_ids = [] + +# init session +d = meraki.DashboardAPI() + +# gather orgs +my_orgs = d.organizations.getOrganizations() +my_orgs = [org for org in my_orgs if org["id"] not in excluded_org_ids] + +print(f"done gathering organizations") + +# gather networks +my_networks = [ + d.organizations.getOrganizationNetworks( + organization["id"], total_pages=all + ) + for organization in my_orgs +] + +print(f"done gathering networks") + +my_appliance_networks = [ + network + for netlist in my_networks + for network in netlist + if network["id"] not in excluded_network_ids + and "appliance" in network["productTypes"] +] + +print(f"done gathering appliance networks") + +# gather routed networks -- appliances in passthrough mode don't have local subnets +my_appliance_routed_networks = [ + network + for network in my_appliance_networks + if d.appliance.getNetworkApplianceSettings(network["id"])["deploymentMode"] + == "routed" +] + +print(f"done gathering routed appliance networks") + +my_appliance_networks_with_vlans = [ + network + for network in my_appliance_routed_networks + if d.appliance.getNetworkApplianceVlansSettings(network["id"])["vlansEnabled"] +] + +print(f"done gathering appliance network vlan settings") + +my_appliance_networks_without_vlans = [ + network + for network in my_appliance_routed_networks + if network not in my_appliance_networks_with_vlans +] + +my_vlan_lists = [ + { + "organizationId": network["organizationId"], + "networkId": network["id"], + "vlans": d.appliance.getNetworkApplianceVlans(network["id"]), + } + for network in my_appliance_networks_with_vlans +] + +print(f"done gathering appliance network vlans") + +my_lans = [ + { + "organizationId": network["organizationId"], + "networkId": network["id"], + "lan": d.appliance.getNetworkApplianceSingleLan(network["id"]), + } + for network in my_appliance_networks_without_vlans +] + +print(f"done gathering appliance network lans") + +# unpack the subnets +vlan_subnets = list() + +for item in my_vlan_lists: + for vlan in item["vlans"]: + this_subnet = dict() + this_subnet["organizationId"] = item["organizationId"] + this_subnet["networkId"] = vlan["networkId"] + this_subnet["subnet"] = vlan["subnet"] + this_subnet["vlanId"] = vlan["id"] + this_subnet["applianceIp"] = vlan["applianceIp"] + vlan_subnets.append(this_subnet) + +lan_subnets = list() + +for item in my_lans: + this_subnet = dict() + this_subnet["organizationId"] = item["organizationId"] + this_subnet["networkId"] = item["networkId"] + this_subnet["subnet"] = item["lan"]["subnet"] + this_subnet["vlanId"] = 0 + this_subnet["applianceIp"] = item["lan"]["applianceIp"] + lan_subnets.append(this_subnet) + +all_subnets = vlan_subnets + lan_subnets + +print("done assembling subnets") + +# dump the subnets to a JSON file +json_object = json.dumps(all_subnets, indent=4) + +with open( + "../../../../../Users/jkuchta/AppData/Roaming/JetBrains/PyCharm2023.2/scratches/subnets.json", + "w", +) as outfile: + outfile.write(json_object) + +print("subnets dumped to subnets.json") From 41fbd1133d6aa97b435523941402e01c30514fd0 Mon Sep 17 00:00:00 2001 From: "John M. Kuchta" Date: Mon, 13 Nov 2023 18:38:39 -0800 Subject: [PATCH 2/4] Update local_subnet_dumper.py --- examples/local_subnet_dumper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/local_subnet_dumper.py b/examples/local_subnet_dumper.py index cfca5a0b..5a4c2874 100644 --- a/examples/local_subnet_dumper.py +++ b/examples/local_subnet_dumper.py @@ -128,7 +128,7 @@ json_object = json.dumps(all_subnets, indent=4) with open( - "../../../../../Users/jkuchta/AppData/Roaming/JetBrains/PyCharm2023.2/scratches/subnets.json", + "subnets.json", "w", ) as outfile: outfile.write(json_object) From 559e4d3edf9be21c4ee2bb4091afe55ba5aa5b5b Mon Sep 17 00:00:00 2001 From: "John M. Kuchta" Date: Fri, 8 Dec 2023 19:16:45 +1100 Subject: [PATCH 3/4] Fix packaging requirement Minimum Python version bumped from 3.7 to 3.8 in setup.py, so subsequent versions of the generated package properly reflect the requirement. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d4cf00c..d25e289a 100644 --- a/setup.py +++ b/setup.py @@ -49,5 +49,5 @@ def find_version(fname): 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', ], - python_requires='>=3.7' + python_requires='>=3.8' ) From 37cd1369bf570c5b575c9498b4f483f460042f43 Mon Sep 17 00:00:00 2001 From: "John M. Kuchta" Date: Fri, 8 Dec 2023 20:14:46 +1100 Subject: [PATCH 4/4] Automatically regenerated library. (#232) Co-authored-by: GitHub Action Fixes #231 --- meraki/__init__.py | 2 +- meraki/aio/api/networks.py | 2 +- meraki/aio/api/organizations.py | 2 +- meraki/aio/api/sm.py | 2 +- meraki/api/networks.py | 2 +- meraki/api/organizations.py | 2 +- meraki/api/sm.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/meraki/__init__.py b/meraki/__init__.py index 2d15f7ec..5164c4f0 100644 --- a/meraki/__init__.py +++ b/meraki/__init__.py @@ -43,7 +43,7 @@ ) from meraki.rest_session import * -__version__ = '1.39.0' +__version__ = '1.39.1' class DashboardAPI(object): diff --git a/meraki/aio/api/networks.py b/meraki/aio/api/networks.py index d598e821..c366bf68 100644 --- a/meraki/aio/api/networks.py +++ b/meraki/aio/api/networks.py @@ -246,7 +246,7 @@ def getNetworkClients(self, networkId: str, total_pages=1, direction='next', **k - direction (string): direction to paginate, either "next" (default) or "prev" page - t0 (string): The beginning of the timespan for the data. The maximum lookback period is 31 days from today. - timespan (number): The timespan for which the information will be fetched. If specifying timespan, do not specify parameter t0. The value must be in seconds and be less than or equal to 31 days. The default is 1 day. - - perPage (integer): The number of entries per page returned. Acceptable range is 3 - 1000. Default is 10. + - perPage (integer): The number of entries per page returned. Acceptable range is 3 - 5000. Default is 10. - startingAfter (string): A token used by the server to indicate the start of the page. Often this is a timestamp or an ID but it is not limited to those. This parameter should not be defined by client applications. The link for the first, last, prev, or next page in the HTTP Link header should define it. - endingBefore (string): A token used by the server to indicate the end of the page. Often this is a timestamp or an ID but it is not limited to those. This parameter should not be defined by client applications. The link for the first, last, prev, or next page in the HTTP Link header should define it. - statuses (array): Filters clients based on status. Can be one of 'Online' or 'Offline'. diff --git a/meraki/aio/api/organizations.py b/meraki/aio/api/organizations.py index 436dd873..78bdde55 100644 --- a/meraki/aio/api/organizations.py +++ b/meraki/aio/api/organizations.py @@ -2117,7 +2117,7 @@ def getOrganizationInventoryDevices(self, organizationId: str, total_pages=1, di - usedState (string): Filter results by used or unused inventory. Accepted values are 'used' or 'unused'. - search (string): Search for devices in inventory based on serial number, mac address, or model. - macs (array): Search for devices in inventory based on mac addresses. - - networkIds (array): Search for devices in inventory based on network ids. + - networkIds (array): Search for devices in inventory based on network ids. Use explicit 'null' value to get available devices only. - serials (array): Search for devices in inventory based on serials. - models (array): Search for devices in inventory based on model. - orderNumbers (array): Search for devices in inventory based on order numbers. diff --git a/meraki/aio/api/sm.py b/meraki/aio/api/sm.py index 7ea3cee8..c4ca80b1 100644 --- a/meraki/aio/api/sm.py +++ b/meraki/aio/api/sm.py @@ -68,7 +68,7 @@ def getNetworkSmDevices(self, networkId: str, total_pages=1, direction='next', * ownerEmail, ownerUsername, osBuild, publicIp, phoneNumber, diskInfoJson, deviceCapacity, isManaged, hadMdm, isSupervised, meid, imei, iccid, simCarrierNetwork, cellularDataUsed, isHotspotEnabled, createdAt, batteryEstCharge, quarantined, avName, avRunning, asName, fwName, isRooted, loginRequired, screenLockEnabled, screenLockDelay, autoLoginDisabled, autoTags, hasMdm, hasDesktopAgent, diskEncryptionEnabled, - hardwareEncryptionCaps, passCodeLock, usesHardwareKeystore, androidSecurityPatchVersion, and url. + hardwareEncryptionCaps, passCodeLock, usesHardwareKeystore, androidSecurityPatchVersion, cellular, and url. - wifiMacs (array): Filter devices by wifi mac(s). - serials (array): Filter devices by serial(s). - ids (array): Filter devices by id(s). diff --git a/meraki/api/networks.py b/meraki/api/networks.py index 6326f52a..898f6682 100644 --- a/meraki/api/networks.py +++ b/meraki/api/networks.py @@ -246,7 +246,7 @@ def getNetworkClients(self, networkId: str, total_pages=1, direction='next', **k - direction (string): direction to paginate, either "next" (default) or "prev" page - t0 (string): The beginning of the timespan for the data. The maximum lookback period is 31 days from today. - timespan (number): The timespan for which the information will be fetched. If specifying timespan, do not specify parameter t0. The value must be in seconds and be less than or equal to 31 days. The default is 1 day. - - perPage (integer): The number of entries per page returned. Acceptable range is 3 - 1000. Default is 10. + - perPage (integer): The number of entries per page returned. Acceptable range is 3 - 5000. Default is 10. - startingAfter (string): A token used by the server to indicate the start of the page. Often this is a timestamp or an ID but it is not limited to those. This parameter should not be defined by client applications. The link for the first, last, prev, or next page in the HTTP Link header should define it. - endingBefore (string): A token used by the server to indicate the end of the page. Often this is a timestamp or an ID but it is not limited to those. This parameter should not be defined by client applications. The link for the first, last, prev, or next page in the HTTP Link header should define it. - statuses (array): Filters clients based on status. Can be one of 'Online' or 'Offline'. diff --git a/meraki/api/organizations.py b/meraki/api/organizations.py index d00caa54..013d1da3 100644 --- a/meraki/api/organizations.py +++ b/meraki/api/organizations.py @@ -2117,7 +2117,7 @@ def getOrganizationInventoryDevices(self, organizationId: str, total_pages=1, di - usedState (string): Filter results by used or unused inventory. Accepted values are 'used' or 'unused'. - search (string): Search for devices in inventory based on serial number, mac address, or model. - macs (array): Search for devices in inventory based on mac addresses. - - networkIds (array): Search for devices in inventory based on network ids. + - networkIds (array): Search for devices in inventory based on network ids. Use explicit 'null' value to get available devices only. - serials (array): Search for devices in inventory based on serials. - models (array): Search for devices in inventory based on model. - orderNumbers (array): Search for devices in inventory based on order numbers. diff --git a/meraki/api/sm.py b/meraki/api/sm.py index 4f6db685..32fcac91 100644 --- a/meraki/api/sm.py +++ b/meraki/api/sm.py @@ -68,7 +68,7 @@ def getNetworkSmDevices(self, networkId: str, total_pages=1, direction='next', * ownerEmail, ownerUsername, osBuild, publicIp, phoneNumber, diskInfoJson, deviceCapacity, isManaged, hadMdm, isSupervised, meid, imei, iccid, simCarrierNetwork, cellularDataUsed, isHotspotEnabled, createdAt, batteryEstCharge, quarantined, avName, avRunning, asName, fwName, isRooted, loginRequired, screenLockEnabled, screenLockDelay, autoLoginDisabled, autoTags, hasMdm, hasDesktopAgent, diskEncryptionEnabled, - hardwareEncryptionCaps, passCodeLock, usesHardwareKeystore, androidSecurityPatchVersion, and url. + hardwareEncryptionCaps, passCodeLock, usesHardwareKeystore, androidSecurityPatchVersion, cellular, and url. - wifiMacs (array): Filter devices by wifi mac(s). - serials (array): Filter devices by serial(s). - ids (array): Filter devices by id(s).