Skip to content

Commit 6d9ada0

Browse files
Improve version check
Also added network concurrency deletion config option.
1 parent c19baa1 commit 6d9ada0

12 files changed

Lines changed: 178 additions & 60 deletions

File tree

generator/generate_library.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import getopt
22
import os
33
import sys
4+
import platform
45

56
import jinja2
67
import requests
@@ -43,6 +44,21 @@ def generate_pagination_parameters(operation: str):
4344
return ret
4445

4546

47+
def check_python_version():
48+
# Check minimum Python version
49+
version_warning_string = f'The generator requires Python 3.10 at minimum. ' \
50+
f'Your interpreter version details are: \n' \
51+
f'platform.python_version_tuple()[0] = {platform.python_version_tuple()[0]}\n' \
52+
f'platform.python_version_tuple()[1] = {platform.python_version_tuple()[1]}\n' \
53+
f'platform.python_version is {platform.python_version()}\n' \
54+
f'Please consult the generator readme at your convenience: ' \
55+
f'https://github.com/meraki/dashboard-api-python/blob/main/generator/readme.md'
56+
if int(platform.python_version_tuple()[0]) != 3:
57+
sys.exit(version_warning_string)
58+
elif int(platform.python_version_tuple()[1]) < 7:
59+
sys.exit(version_warning_string)
60+
61+
4662
# Returns full link to endpoint's documentation on Developer Hub
4763
# Note: updates to the documentation site may impact these URLs.
4864
def docs_url(operation: str):
@@ -615,14 +631,7 @@ def main(inputs):
615631
if arg.lower() == 'true':
616632
is_github_action = True
617633

618-
python_version_warning_string = f'The generator requires Python 3.10 at minimum, but your interpreter version is ' \
619-
f'{sys.version}. Please consult the generator readme at your convenience: ' \
620-
f'https://github.com/meraki/dashboard-api-python/blob/main/generator/readme.md'
621-
# Check minimum Python version
622-
if sys.version_info[0] != 3:
623-
sys.exit(python_version_warning_string)
624-
elif sys.version_info[1] < 10:
625-
sys.exit(python_version_warning_string)
634+
check_python_version()
626635

627636
# Retrieve latest OpenAPI specification
628637
if org_id:

meraki/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
USE_ITERATOR_FOR_GET_PAGES,
4646
)
4747

48-
__version__ = '1.34.2'
48+
__version__ = '1.34.3'
4949

5050

5151
class DashboardAPI(object):
@@ -60,6 +60,7 @@ class DashboardAPI(object):
6060
- wait_on_rate_limit (boolean): retry if 429 rate limit error encountered?
6161
- nginx_429_retry_wait_time (integer): Nginx 429 retry wait time
6262
- action_batch_retry_wait_time (integer): action batch concurrency error retry wait time
63+
- network_delete_retry_wait_time (integer): network deletion concurrency error retry wait time
6364
- retry_4xx_error (boolean): retry if encountering other 4XX error (besides 429)?
6465
- retry_4xx_error_wait_time (integer): other 4XX error retry wait time
6566
- maximum_retries (integer): retry up to this many times when encountering 429s or other server-side errors
@@ -84,6 +85,7 @@ def __init__(self,
8485
wait_on_rate_limit=WAIT_ON_RATE_LIMIT,
8586
nginx_429_retry_wait_time=NGINX_429_RETRY_WAIT_TIME,
8687
action_batch_retry_wait_time=ACTION_BATCH_RETRY_WAIT_TIME,
88+
network_delete_retry_wait_time=NETWORK_DELETE_RETRY_WAIT_TIME,
8789
retry_4xx_error=RETRY_4XX_ERROR,
8890
retry_4xx_error_wait_time=RETRY_4XX_ERROR_WAIT_TIME,
8991
maximum_retries=MAXIMUM_RETRIES,
@@ -157,6 +159,7 @@ def __init__(self,
157159
wait_on_rate_limit=wait_on_rate_limit,
158160
nginx_429_retry_wait_time=nginx_429_retry_wait_time,
159161
action_batch_retry_wait_time=action_batch_retry_wait_time,
162+
network_delete_retry_wait_time=network_delete_retry_wait_time,
160163
retry_4xx_error=retry_4xx_error,
161164
retry_4xx_error_wait_time=retry_4xx_error_wait_time,
162165
maximum_retries=maximum_retries,

meraki/aio/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22
import os
3-
from datetime import datetime
43

54
from meraki.aio.rest_session import *
65
from meraki.aio.api.administered import AsyncAdministered
@@ -30,6 +29,7 @@
3029
WAIT_ON_RATE_LIMIT,
3130
NGINX_429_RETRY_WAIT_TIME,
3231
ACTION_BATCH_RETRY_WAIT_TIME,
32+
NETWORK_DELETE_RETRY_WAIT_TIME,
3333
RETRY_4XX_ERROR,
3434
RETRY_4XX_ERROR_WAIT_TIME,
3535
MAXIMUM_RETRIES,
@@ -59,6 +59,7 @@ class AsyncDashboardAPI:
5959
- wait_on_rate_limit (boolean): retry if 429 rate limit error encountered?
6060
- nginx_429_retry_wait_time (integer): Nginx 429 retry wait time
6161
- action_batch_retry_wait_time (integer): action batch concurrency error retry wait time
62+
- network_delete_retry_wait_time (integer): network deletion concurrency error retry wait time
6263
- retry_4xx_error (boolean): retry if encountering other 4XX error (besides 429)?
6364
- retry_4xx_error_wait_time (integer): other 4XX error retry wait time
6465
- maximum_retries (integer): retry up to this many times when encountering 429s or other server-side errors
@@ -84,6 +85,7 @@ def __init__(self,
8485
wait_on_rate_limit=WAIT_ON_RATE_LIMIT,
8586
nginx_429_retry_wait_time=NGINX_429_RETRY_WAIT_TIME,
8687
action_batch_retry_wait_time=ACTION_BATCH_RETRY_WAIT_TIME,
88+
network_delete_retry_wait_time=NETWORK_DELETE_RETRY_WAIT_TIME,
8789
retry_4xx_error=RETRY_4XX_ERROR,
8890
retry_4xx_error_wait_time=RETRY_4XX_ERROR_WAIT_TIME,
8991
maximum_retries=MAXIMUM_RETRIES,
@@ -156,6 +158,7 @@ def __init__(self,
156158
wait_on_rate_limit=wait_on_rate_limit,
157159
nginx_429_retry_wait_time=nginx_429_retry_wait_time,
158160
action_batch_retry_wait_time=action_batch_retry_wait_time,
161+
network_delete_retry_wait_time=network_delete_retry_wait_time,
159162
retry_4xx_error=retry_4xx_error,
160163
retry_4xx_error_wait_time=retry_4xx_error_wait_time,
161164
maximum_retries=maximum_retries,

meraki/aio/api/camera.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,27 @@ def createNetworkCameraQualityRetentionProfile(self, networkId: str, name: str,
505505

506506

507507

508+
def getNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str):
509+
"""
510+
**Retrieve a single quality retention profile**
511+
https://developer.cisco.com/meraki/api-v1/#!get-network-camera-quality-retention-profile
512+
513+
- networkId (string): Network ID
514+
- qualityRetentionProfileId (string): Quality retention profile ID
515+
"""
516+
517+
metadata = {
518+
'tags': ['camera', 'configure', 'qualityRetentionProfiles'],
519+
'operation': 'getNetworkCameraQualityRetentionProfile'
520+
}
521+
networkId = urllib.parse.quote(str(networkId), safe='')
522+
qualityRetentionProfileId = urllib.parse.quote(str(qualityRetentionProfileId), safe='')
523+
resource = f'/networks/{networkId}/camera/qualityRetentionProfiles/{qualityRetentionProfileId}'
524+
525+
return self._session.get(metadata, resource)
526+
527+
528+
508529
def updateNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str, **kwargs):
509530
"""
510531
**Update an existing quality retention profile for this network.**
@@ -561,27 +582,6 @@ def deleteNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRete
561582

562583

563584

564-
def getNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str):
565-
"""
566-
**Retrieve a single quality retention profile**
567-
https://developer.cisco.com/meraki/api-v1/#!get-network-camera-quality-retention-profile
568-
569-
- networkId (string): Network ID
570-
- qualityRetentionProfileId (string): Quality retention profile ID
571-
"""
572-
573-
metadata = {
574-
'tags': ['camera', 'configure', 'qualityRetentionProfiles'],
575-
'operation': 'getNetworkCameraQualityRetentionProfile'
576-
}
577-
networkId = urllib.parse.quote(str(networkId), safe='')
578-
qualityRetentionProfileId = urllib.parse.quote(str(qualityRetentionProfileId), safe='')
579-
resource = f'/networks/{networkId}/camera/qualityRetentionProfiles/{qualityRetentionProfileId}'
580-
581-
return self._session.get(metadata, resource)
582-
583-
584-
585585
def getNetworkCameraSchedules(self, networkId: str):
586586
"""
587587
**Returns a list of all camera recording schedules.**

meraki/aio/api/devices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def getDeviceLldpCdp(self, serial: str):
272272

273273
def getDeviceLossAndLatencyHistory(self, serial: str, ip: str, **kwargs):
274274
"""
275-
**Get the uplink loss percentage and latency in milliseconds, and goodput in kilobits per second for a wired network device.**
275+
**Get the uplink loss percentage and latency in milliseconds, and goodput in kilobits per second for MX, MG and Z devices.**
276276
https://developer.cisco.com/meraki/api-v1/#!get-device-loss-and-latency-history
277277
278278
- serial (string): Serial

meraki/aio/api/organizations.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,6 +1576,48 @@ def getOrganizationDevicesAvailabilities(self, organizationId: str, total_pages=
15761576

15771577

15781578

1579+
def getOrganizationDevicesAvailabilitiesChangeHistory(self, organizationId: str, total_pages=1, direction='next', **kwargs):
1580+
"""
1581+
**List the availability history information for devices in an organization.**
1582+
https://developer.cisco.com/meraki/api-v1/#!get-organization-devices-availabilities-change-history
1583+
1584+
- organizationId (string): Organization ID
1585+
- total_pages (integer or string): use with perPage to get total results up to total_pages*perPage; -1 or "all" for all pages
1586+
- direction (string): direction to paginate, either "next" (default) or "prev" page
1587+
- perPage (integer): The number of entries per page returned. Acceptable range is 3 - 1000. Default is 1000.
1588+
- 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.
1589+
- 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.
1590+
- t0 (string): The beginning of the timespan for the data. The maximum lookback period is 14 days from today.
1591+
- t1 (string): The end of the timespan for the data. t1 can be a maximum of 14 days after t0.
1592+
- timespan (number): The timespan for which the information will be fetched. If specifying timespan, do not specify parameters t0 and t1. The value must be in seconds and be less than or equal to 14 days. The default is 1 day.
1593+
- serials (array): Optional parameter to filter device availabilities history by device serial numbers
1594+
- productTypes (array): Optional parameter to filter device availabilities history by device product types
1595+
- networkIds (array): Optional parameter to filter device availabilities history by network IDs
1596+
- statuses (array): Optional parameter to filter device availabilities history by device statuses
1597+
"""
1598+
1599+
kwargs.update(locals())
1600+
1601+
metadata = {
1602+
'tags': ['organizations', 'monitor', 'devices', 'availabilities', 'changeHistory'],
1603+
'operation': 'getOrganizationDevicesAvailabilitiesChangeHistory'
1604+
}
1605+
organizationId = urllib.parse.quote(str(organizationId), safe='')
1606+
resource = f'/organizations/{organizationId}/devices/availabilities/changeHistory'
1607+
1608+
query_params = ['perPage', 'startingAfter', 'endingBefore', 't0', 't1', 'timespan', 'serials', 'productTypes', 'networkIds', 'statuses', ]
1609+
params = {k.strip(): v for k, v in kwargs.items() if k.strip() in query_params}
1610+
1611+
array_params = ['serials', 'productTypes', 'networkIds', 'statuses', ]
1612+
for k, v in kwargs.items():
1613+
if k.strip() in array_params:
1614+
params[f'{k.strip()}[]'] = kwargs[f'{k}']
1615+
params.pop(k.strip())
1616+
1617+
return self._session.get_pages(metadata, resource, params, total_pages, direction)
1618+
1619+
1620+
15791621
def getOrganizationDevicesPowerModulesStatusesByDevice(self, organizationId: str, total_pages=1, direction='next', **kwargs):
15801622
"""
15811623
**List the power status information for devices in an organization**

meraki/aio/rest_session.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def __init__(
2929
wait_on_rate_limit=WAIT_ON_RATE_LIMIT,
3030
nginx_429_retry_wait_time=NGINX_429_RETRY_WAIT_TIME,
3131
action_batch_retry_wait_time=ACTION_BATCH_RETRY_WAIT_TIME,
32+
network_delete_retry_wait_time=NETWORK_DELETE_RETRY_WAIT_TIME,
3233
retry_4xx_error=RETRY_4XX_ERROR,
3334
retry_4xx_error_wait_time=RETRY_4XX_ERROR_WAIT_TIME,
3435
maximum_retries=MAXIMUM_RETRIES,
@@ -50,6 +51,7 @@ def __init__(
5051
self._wait_on_rate_limit = wait_on_rate_limit
5152
self._nginx_429_retry_wait_time = nginx_429_retry_wait_time
5253
self._action_batch_retry_wait_time = action_batch_retry_wait_time
54+
self._network_delete_retry_wait_time = network_delete_retry_wait_time
5355
self._retry_4xx_error = retry_4xx_error
5456
self._retry_4xx_error_wait_time = retry_4xx_error_wait_time
5557
self._maximum_retries = maximum_retries
@@ -249,15 +251,27 @@ async def _request(self, metadata, method, url, **kwargs):
249251
except aiohttp.client_exceptions.ContentTypeError:
250252
try:
251253
message = (await response.text())[:100]
254+
message_is_dict = True
252255
except:
253256
message = None
257+
message_is_dict = False
254258

255-
# Check specifically for action batch concurrency error
256-
action_batch_concurrency_error = {
257-
"errors": [
258-
"Too many concurrently executing batches. Maximum is 5 confirmed but not yet executed batches."
259-
]
259+
# Check for specific concurrency errors
260+
network_delete_concurrency_error_text = 'This may be due to concurrent requests to delete networks.'
261+
action_batch_concurrency_error = {'errors': [
262+
'Too many concurrently executing batches. Maximum is 5 confirmed but not yet executed batches.']
260263
}
264+
# Check specifically for network delete concurrency error
265+
if message_is_dict and 'errors' in message.keys() \
266+
and network_delete_concurrency_error_text in message['errors'][0]:
267+
wait = self._network_delete_retry_wait_time
268+
if self._logger:
269+
self._logger.warning(f'{tag}, {operation} - {status} {reason}, retrying in {wait} seconds')
270+
time.sleep(wait)
271+
retries -= 1
272+
if retries == 0:
273+
raise APIError(metadata, response)
274+
# Check specifically for action batch concurrency error
261275
if message == action_batch_concurrency_error:
262276
wait = self._action_batch_retry_wait_time
263277
if self._logger:

meraki/api/camera.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,27 @@ def createNetworkCameraQualityRetentionProfile(self, networkId: str, name: str,
505505

506506

507507

508+
def getNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str):
509+
"""
510+
**Retrieve a single quality retention profile**
511+
https://developer.cisco.com/meraki/api-v1/#!get-network-camera-quality-retention-profile
512+
513+
- networkId (string): Network ID
514+
- qualityRetentionProfileId (string): Quality retention profile ID
515+
"""
516+
517+
metadata = {
518+
'tags': ['camera', 'configure', 'qualityRetentionProfiles'],
519+
'operation': 'getNetworkCameraQualityRetentionProfile'
520+
}
521+
networkId = urllib.parse.quote(str(networkId), safe='')
522+
qualityRetentionProfileId = urllib.parse.quote(str(qualityRetentionProfileId), safe='')
523+
resource = f'/networks/{networkId}/camera/qualityRetentionProfiles/{qualityRetentionProfileId}'
524+
525+
return self._session.get(metadata, resource)
526+
527+
528+
508529
def updateNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str, **kwargs):
509530
"""
510531
**Update an existing quality retention profile for this network.**
@@ -561,27 +582,6 @@ def deleteNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRete
561582

562583

563584

564-
def getNetworkCameraQualityRetentionProfile(self, networkId: str, qualityRetentionProfileId: str):
565-
"""
566-
**Retrieve a single quality retention profile**
567-
https://developer.cisco.com/meraki/api-v1/#!get-network-camera-quality-retention-profile
568-
569-
- networkId (string): Network ID
570-
- qualityRetentionProfileId (string): Quality retention profile ID
571-
"""
572-
573-
metadata = {
574-
'tags': ['camera', 'configure', 'qualityRetentionProfiles'],
575-
'operation': 'getNetworkCameraQualityRetentionProfile'
576-
}
577-
networkId = urllib.parse.quote(str(networkId), safe='')
578-
qualityRetentionProfileId = urllib.parse.quote(str(qualityRetentionProfileId), safe='')
579-
resource = f'/networks/{networkId}/camera/qualityRetentionProfiles/{qualityRetentionProfileId}'
580-
581-
return self._session.get(metadata, resource)
582-
583-
584-
585585
def getNetworkCameraSchedules(self, networkId: str):
586586
"""
587587
**Returns a list of all camera recording schedules.**

meraki/api/devices.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def getDeviceLldpCdp(self, serial: str):
272272

273273
def getDeviceLossAndLatencyHistory(self, serial: str, ip: str, **kwargs):
274274
"""
275-
**Get the uplink loss percentage and latency in milliseconds, and goodput in kilobits per second for a wired network device.**
275+
**Get the uplink loss percentage and latency in milliseconds, and goodput in kilobits per second for MX, MG and Z devices.**
276276
https://developer.cisco.com/meraki/api-v1/#!get-device-loss-and-latency-history
277277
278278
- serial (string): Serial

0 commit comments

Comments
 (0)