Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/generation-report.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Generation Report

## 2026-07-29 | Library v4.3.0b4 | API 1.72.0-beta.4


No Python keyword parameter conflicts detected.


## 2026-07-22 | Library v4.3.0b3 | API 1.72.0-beta.3


Expand Down
40 changes: 20 additions & 20 deletions meraki/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import logging
import os
from datetime import datetime

from meraki._version import __version__ # noqa: F401
from meraki.api.administered import Administered
from meraki.api.appliance import Appliance

Expand All @@ -23,36 +25,34 @@

# Config import
from meraki.config import (
ACTION_BATCH_RETRY_WAIT_TIME,
API_KEY_ENVIRONMENT_VARIABLE,
DEFAULT_BASE_URL,
SINGLE_REQUEST_TIMEOUT,
BE_GEO_ID,
CERTIFICATE_PATH,
REQUESTS_PROXY,
WAIT_ON_RATE_LIMIT,
NGINX_429_RETRY_WAIT_TIME,
ACTION_BATCH_RETRY_WAIT_TIME,
NETWORK_DELETE_RETRY_WAIT_TIME,
RETRY_4XX_ERROR,
RETRY_4XX_ERROR_WAIT_TIME,
DEFAULT_BASE_URL,
INHERIT_LOGGING_CONFIG,
LOG_FILE_PREFIX,
LOG_PATH,
MAXIMUM_RETRIES,
MERAKI_PYTHON_SDK_CALLER,
NETWORK_DELETE_RETRY_WAIT_TIME,
NGINX_429_RETRY_WAIT_TIME,
OUTPUT_LOG,
LOG_PATH,
LOG_FILE_PREFIX,
PRINT_TO_CONSOLE,
SUPPRESS_LOGGING,
INHERIT_LOGGING_CONFIG,
REQUESTS_PROXY,
RETRY_4XX_ERROR,
RETRY_4XX_ERROR_WAIT_TIME,
SIMULATE_API_CALLS,
BE_GEO_ID,
MERAKI_PYTHON_SDK_CALLER,
SINGLE_REQUEST_TIMEOUT,
SUPPRESS_LOGGING,
USE_ITERATOR_FOR_GET_PAGES,
VALIDATE_KWARGS,
WAIT_ON_RATE_LIMIT,
)
from meraki.rest_session import RestSession
from meraki.exceptions import APIError, APIKeyError, APIResponseError, AsyncAPIError
from meraki._version import __version__ # noqa: F401
from datetime import datetime
from meraki.rest_session import RestSession

__api_version__ = "1.72.0-beta.3"
__api_version__ = "1.72.0-beta.4"

__all__ = [
"APIError",
Expand All @@ -63,7 +63,7 @@
]


class DashboardAPI(object):
class DashboardAPI:
"""
**Creates a persistent Meraki dashboard API session**

Expand Down
2 changes: 1 addition & 1 deletion meraki/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "4.3.0b3"
__version__ = "4.3.0b4"
36 changes: 18 additions & 18 deletions meraki/aio/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import os
from datetime import datetime

from meraki.aio.api.administered import AsyncAdministered
from meraki.aio.api.appliance import AsyncAppliance
Expand All @@ -18,39 +19,38 @@
from meraki.aio.api.wireless import AsyncWireless
from meraki.aio.api.wirelessController import AsyncWirelessController
from meraki.aio.rest_session import AsyncRestSession
from meraki.exceptions import APIKeyError
from datetime import datetime

# Batch class imports
from meraki.api.batch import Batch

# Config import
from meraki.config import (
ACTION_BATCH_RETRY_WAIT_TIME,
AIO_MAXIMUM_CONCURRENT_REQUESTS,
API_KEY_ENVIRONMENT_VARIABLE,
DEFAULT_BASE_URL,
SINGLE_REQUEST_TIMEOUT,
BE_GEO_ID,
CERTIFICATE_PATH,
REQUESTS_PROXY,
WAIT_ON_RATE_LIMIT,
NGINX_429_RETRY_WAIT_TIME,
ACTION_BATCH_RETRY_WAIT_TIME,
NETWORK_DELETE_RETRY_WAIT_TIME,
RETRY_4XX_ERROR,
RETRY_4XX_ERROR_WAIT_TIME,
DEFAULT_BASE_URL,
INHERIT_LOGGING_CONFIG,
LOG_FILE_PREFIX,
LOG_PATH,
MAXIMUM_RETRIES,
MERAKI_PYTHON_SDK_CALLER,
NETWORK_DELETE_RETRY_WAIT_TIME,
NGINX_429_RETRY_WAIT_TIME,
OUTPUT_LOG,
LOG_PATH,
LOG_FILE_PREFIX,
PRINT_TO_CONSOLE,
SUPPRESS_LOGGING,
INHERIT_LOGGING_CONFIG,
REQUESTS_PROXY,
RETRY_4XX_ERROR,
RETRY_4XX_ERROR_WAIT_TIME,
SIMULATE_API_CALLS,
BE_GEO_ID,
MERAKI_PYTHON_SDK_CALLER,
SINGLE_REQUEST_TIMEOUT,
SUPPRESS_LOGGING,
USE_ITERATOR_FOR_GET_PAGES,
AIO_MAXIMUM_CONCURRENT_REQUESTS,
VALIDATE_KWARGS,
WAIT_ON_RATE_LIMIT,
)
from meraki.exceptions import APIKeyError


class AsyncDashboardAPI:
Expand Down
4 changes: 2 additions & 2 deletions meraki/aio/api/appliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ def updateNetworkAppliancePort(self, networkId: str, portId: str, **kwargs):
- type (string): The type of the port: 'access' or 'trunk'.
- vlan (integer): Native VLAN when the port is in Trunk mode. Access VLAN when the port is in Access mode.
- allowedVlans (string): Comma-delimited list of VLAN IDs (e.g. '2,15') for all devices. Secure Routers also support VLAN ranges (e.g. '2-10,15'). Use 'all' to permit all VLANs on the port.
- accessPolicy (string): The name of the policy. Only applicable to Access ports. Valid values are: 'open', '8021x-radius', 'mac-radius', 'hybris-radius' for MX64 or Z3 or any MX supporting the per port authentication feature. Otherwise, 'open' is the only valid value and 'open' is the default value if the field is missing.
- accessPolicy (string): The name of the policy. Only applicable to Access ports. Valid values are: 'open', '8021x-radius', 'mac-radius', 'hybrid-radius', 'access-manager' for MX64 or Z3 or any MX supporting the per port authentication feature. Otherwise, 'open' is the only valid value and 'open' is the default value if the field is missing.
- peerSgtCapable (boolean): If true, Peer SGT is enabled for traffic through this port. Applicable to trunk port only, not access port.
- adaptivePolicyGroupId (string): Adaptive policy group ID that all traffic originating from this port is assigned to.
- sgt (object): Security Group Tag settings for the port.
Expand Down Expand Up @@ -3274,7 +3274,7 @@ def updateNetworkApplianceVpnBgp(self, networkId: str, enabled: bool, **kwargs):

kwargs.update(locals())

if "priorityRoute" in kwargs and kwargs["priorityRoute"] is not None:
if "priorityRoute" in kwargs:
options = ["Auto VPN", "eBGP"]
assert kwargs["priorityRoute"] in options, (
f'''"priorityRoute" cannot be "{kwargs["priorityRoute"]}", & must be set to one of: {options}'''
Expand Down
54 changes: 54 additions & 0 deletions meraki/aio/api/campusGateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,60 @@ def getOrganizationCampusGatewayClustersSsids(self, organizationId: str, total_p

return self._session.get_pages(metadata, resource, params, total_pages, direction)

def getOrganizationCampusGatewayClustersTunnelable(
self, organizationId: str, fromNetworkIds: list, total_pages=1, direction="next", **kwargs
):
"""
**List available campus gateway clusters that can be used for wireless network tunneling**
https://developer.cisco.com/meraki/api-v1/#!get-organization-campus-gateway-clusters-tunnelable

- organizationId (string): Organization ID
- fromNetworkIds (array): Filter results by wireless network IDs.
- total_pages (integer or string): use with perPage to get total results up to total_pages*perPage; -1 or "all" for all pages
- direction (string): direction to paginate, either "next" (default) or "prev" page
- excludeSsidNumbers (array): Optional SSID numbers (0-15) to exclude from restriction checks, corresponding to each fromNetworkIds entry. Only one SSID per network can be excluded.
- perPage (integer): The number of entries per page returned. Acceptable range is 3 - 200. Default is 200.
- 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.
"""

kwargs.update(locals())

metadata = {
"tags": ["campusGateway", "configure", "clusters", "tunnelable"],
"operation": "getOrganizationCampusGatewayClustersTunnelable",
}
organizationId = urllib.parse.quote(str(organizationId), safe="")
resource = f"/organizations/{organizationId}/campusGateway/clusters/tunnelable"

query_params = [
"fromNetworkIds",
"excludeSsidNumbers",
"perPage",
"startingAfter",
"endingBefore",
]
params = {k.strip(): v for k, v in kwargs.items() if k.strip() in query_params}

array_params = [
"fromNetworkIds",
"excludeSsidNumbers",
]
for k, v in kwargs.items():
if k.strip() in array_params:
params[f"{k.strip()}[]"] = kwargs[f"{k}"]
params.pop(k.strip())

if self._session._validate_kwargs:
all_params = query_params + array_params
invalid = [k for k in kwargs if k.strip() not in all_params and k != "self"]
if invalid and self._session._logger:
self._session._logger.warning(
f"getOrganizationCampusGatewayClustersTunnelable: ignoring unrecognized kwargs: {invalid}"
)

return self._session.get_pages(metadata, resource, params, total_pages, direction)

def getOrganizationCampusGatewayClustersTunnelingByClusterByNetwork(
self, organizationId: str, total_pages=1, direction="next", **kwargs
):
Expand Down
9 changes: 3 additions & 6 deletions meraki/aio/api/networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,7 @@ def createNetworkFirmwareUpgradesRollback(self, networkId: str, reasons: list, *
options = [
"appliance",
"camera",
"campusGateway",
"cellularGateway",
"secureConnect",
"switch",
Expand Down Expand Up @@ -3321,10 +3322,9 @@ def createNetworkVlanProfile(self, networkId: str, name: str, vlanNames: list, v
- vlanNames (array): An array of named VLANs
- vlanGroups (array): An array of VLAN groups
- iname (string): IName of the profile
- allowedVlans (string): The VLANs allowed on the VLAN profile. Only applicable to trunk ports. The given range must be inclusive of all named VLANs.
"""

kwargs.update(locals())
kwargs = locals()

metadata = {
"tags": ["networks", "configure", "vlanProfiles"],
Expand All @@ -3335,7 +3335,6 @@ def createNetworkVlanProfile(self, networkId: str, name: str, vlanNames: list, v

body_params = [
"name",
"allowedVlans",
"vlanNames",
"vlanGroups",
"iname",
Expand Down Expand Up @@ -3471,10 +3470,9 @@ def updateNetworkVlanProfile(self, networkId: str, iname: str, name: str, vlanNa
- name (string): Name of the profile, string length must be from 1 to 255 characters
- vlanNames (array): An array of named VLANs
- vlanGroups (array): An array of VLAN groups
- allowedVlans (string): The VLANs allowed on the VLAN profile. Only applicable to trunk ports. The given range must be inclusive of all named VLANs.
"""

kwargs.update(locals())
kwargs = locals()

metadata = {
"tags": ["networks", "configure", "vlanProfiles"],
Expand All @@ -3486,7 +3484,6 @@ def updateNetworkVlanProfile(self, networkId: str, iname: str, name: str, vlanNa

body_params = [
"name",
"allowedVlans",
"vlanNames",
"vlanGroups",
]
Expand Down
13 changes: 12 additions & 1 deletion meraki/aio/api/organizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12035,7 +12035,18 @@ def detachOrganizationSaseSites(self, organizationId: str, **kwargs):
organizationId = urllib.parse.quote(str(organizationId), safe="")
resource = f"/organizations/{organizationId}/sase/sites/detach"

return self._session.delete(metadata, resource)
body_params = [
"items",
]
payload = {k.strip(): v for k, v in kwargs.items() if k.strip() in body_params}

if self._session._validate_kwargs:
all_params = [] + body_params
invalid = [k for k in kwargs if k.strip() not in all_params and k != "self"]
if invalid and self._session._logger:
self._session._logger.warning(f"detachOrganizationSaseSites: ignoring unrecognized kwargs: {invalid}")

return self._session.post(metadata, resource, payload)

def updateOrganizationSaseSite(self, organizationId: str, siteId: str, **kwargs):
"""
Expand Down
4 changes: 2 additions & 2 deletions meraki/aio/api/wireless.py
Original file line number Diff line number Diff line change
Expand Up @@ -5468,7 +5468,7 @@ def getOrganizationAssuranceWirelessExperienceMetricsOverviewHistoryByNetwork(

def getOrganizationAssuranceWirelessExperienceMostImpactedNetworks(self, organizationId: str, **kwargs):
"""
**Returns the most impacted wireless experience networks and the top failure contributor for each network.**
**Returns the most impacted wireless experience networks with client counts, impacted experience metrics, and the top failure contributor for each network.**
https://developer.cisco.com/meraki/api-v1/#!get-organization-assurance-wireless-experience-most-impacted-networks

- organizationId (string): Organization ID
Expand Down Expand Up @@ -5511,7 +5511,7 @@ def getOrganizationAssuranceWirelessExperienceMostImpactedNetworks(self, organiz

def getOrganizationAssuranceWirelessExperienceMostImpactedXMs(self, organizationId: str, **kwargs):
"""
**Returns the most impacted wireless experience metrics and the top failure contributor for each metric/network pair.**
**Returns the most impacted wireless experience metrics, including client counts, top failure contributor, and most impacted networks for each metric.**
https://developer.cisco.com/meraki/api-v1/#!get-organization-assurance-wireless-experience-most-impacted-x-ms

- organizationId (string): Organization ID
Expand Down
18 changes: 5 additions & 13 deletions meraki/aio/rest_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,7 @@ async def _get_pages_iterator(
starting_after = urllib.parse.unquote(str(links["next"]["url"]).split("startingAfter=")[1])
delta = datetime.utcnow() - datetime.fromisoformat(starting_after[:-1])
# Break out of loop if startingAfter returned from next link is within 5 minutes of current time
if delta.total_seconds() < 300:
break
# Or if next page is past the specified window's end time
elif event_log_end_time and starting_after > event_log_end_time:
if delta.total_seconds() < 300 or event_log_end_time and starting_after > event_log_end_time:
break

metadata["page"] += 1
Expand Down Expand Up @@ -433,10 +430,7 @@ async def _get_pages_legacy(
starting_after = urllib.parse.unquote(str(links["next"]["url"]).split("startingAfter=")[1])
delta = datetime.utcnow() - datetime.fromisoformat(starting_after[:-1])
# Break out of loop if startingAfter returned from next link is within 5 minutes of current time
if delta.total_seconds() < 300:
break
# Or if next page is past the specified window's end time
elif event_log_end_time and starting_after > event_log_end_time:
if delta.total_seconds() < 300 or event_log_end_time and starting_after > event_log_end_time:
break

metadata["page"] += 1
Expand Down Expand Up @@ -472,10 +466,8 @@ async def _get_pages_legacy(
events = json_response["events"]
if direction == "next":
events = events[::-1]
if start < results["pageStartAt"]:
results["pageStartAt"] = start
if end > results["pageEndAt"]:
results["pageEndAt"] = end
results["pageStartAt"] = min(results["pageStartAt"], start)
results["pageEndAt"] = max(results["pageEndAt"], end)
results["events"].extend(events)

total_pages = total_pages - 1
Expand All @@ -501,7 +493,7 @@ async def delete(self, metadata, url, params=None):
metadata["url"] = url
metadata["params"] = params
async with await self.request(metadata, "DELETE", url, params=params):
return None
return

async def close(self):
await self._req_session.close()
4 changes: 2 additions & 2 deletions meraki/api/administered.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import urllib


class Administered(object):
class Administered:
def __init__(self, session):
super(Administered, self).__init__()
super().__init__()
self._session = session

def getAdministeredIdentitiesMe(self):
Expand Down
Loading
Loading