Please direct me to the correct Group if this is incorrect. After searching, this thread is the closest I have found to my issue. I am using the Automation-scripts-Master Python Script for retrieving an inventory of each of my organizations networks but it has started to miss entire organizations. My API key is valid and I can pull the orgs missed if I use py inventorycvs.py -k <myapikey> -o "name" -f c:\temp\listofitems.csv I get a list of the Org/networks/items but it is missing orgs. import sys, getopt, requests, time, datetime, os, smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from urllib.parse import urlencode
from requests import Session, utils
class NoRebuildAuthSession(Session):
def rebuild_auth(self, prepared_request, response):
"""Prevent auth header stripping on redirect."""
API_MAX_RETRIES = 3
API_CONNECT_TIMEOUT = 60
API_TRANSMIT_TIMEOUT = 60
API_STATUS_RATE_LIMIT = 429
API_RETRY_DEFAULT_WAIT = 3
FLAG_REQUEST_VERBOSE = True
API_BASE_URL = "https://api.meraki.com/api/v1"
API_KEY_ENV_VAR_NAME = "MERAKI_DASHBOARD_API_KEY"
def send_email(smtp_server, port, sender_email, sender_password, recipient_email, subject, body, attachment_path):
try:
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = recipient_email
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
if attachment_path:
with open(attachment_path, "rb") as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
"Content-Disposition",
f"attachment; filename={attachment_path.split('/')[-1]}",
)
msg.attach(part)
server = smtplib.SMTP(smtp_server, port)
server.starttls()
server.login(sender_email, sender_password)
server.send_message(msg)
server.quit()
print(f"Email sent successfully to {recipient_email}")
except Exception as e:
print(f"Failed to send email: {e}")
def merakiRequest(p_apiKey, p_httpVerb, p_endpoint, p_additionalHeaders=None, p_queryItems=None,
p_requestBody=None, p_verbose=False, p_retry=0):
if p_retry > API_MAX_RETRIES:
if(p_verbose):
print("ERROR: Reached max retries")
return False, None, None, None
bearerString = "Bearer " + str(p_apiKey)
headers = {"Authorization": bearerString}
if not p_additionalHeaders is None:
headers.update(p_additionalHeaders)
query = ""
if not p_queryItems is None:
qArrayFix = {}
for item in p_queryItems:
if isinstance(p_queryItems[item], list):
qArrayFix["%s[]" % item] = p_queryItems[item]
else:
qArrayFix[item] = p_queryItems[item]
query = "?" + urlencode(qArrayFix, True)
url = API_BASE_URL + p_endpoint + query
verb = p_httpVerb.upper()
session = NoRebuildAuthSession()
verbs = {
'DELETE' : { 'function': session.delete, 'hasBody': False },
'GET' : { 'function': session.get, 'hasBody': False },
'POST' : { 'function': session.post, 'hasBody': True },
'PUT' : { 'function': session.put, 'hasBody': True }
}
try:
if(p_verbose):
print(verb, url)
if verb in verbs:
if verbs[verb]['hasBody'] and not p_requestBody is None:
r = verbs[verb]['function'](
url,
headers = headers,
json = p_requestBody,
timeout = (API_CONNECT_TIMEOUT, API_TRANSMIT_TIMEOUT)
)
else:
r = verbs[verb]['function'](
url,
headers = headers,
timeout = (API_CONNECT_TIMEOUT, API_TRANSMIT_TIMEOUT)
)
else:
return False, None, None, None
except:
return False, None, None, None
if(p_verbose):
print(r.status_code)
success = r.status_code in range (200, 299)
errors = None
responseHeaders = None
responseBody = None
if r.status_code == API_STATUS_RATE_LIMIT:
retryInterval = API_RETRY_DEFAULT_WAIT
if "Retry-After" in r.headers:
retryInterval = r.headers["Retry-After"]
if "retry-after" in r.headers:
retryInterval = r.headers["retry-after"]
if(p_verbose):
print("INFO: Hit max request rate. Retrying %s after %s seconds" % (p_retry+1, retryInterval))
time.sleep(int(retryInterval))
success, errors, responseHeaders, responseBody = merakiRequest(p_apiKey, p_httpVerb, p_endpoint, p_additionalHeaders,
p_queryItems, p_requestBody, p_verbose, p_retry+1)
return success, errors, responseHeaders, responseBody
try:
rjson = r.json()
except:
rjson = None
if not rjson is None:
if "errors" in rjson:
errors = rjson["errors"]
if(p_verbose):
print(errors)
else:
responseBody = rjson
if "Link" in r.headers:
parsedLinks = utils.parse_header_links(r.headers["Link"])
for link in parsedLinks:
if link["rel"] == "next":
if(p_verbose):
print("Next page:", link["url"])
splitLink = link["url"].split("/api/v1")
success, errors, responseHeaders, nextBody = merakiRequest(p_apiKey, p_httpVerb, splitLink[1],
p_additionalHeaders=p_additionalHeaders,
p_requestBody=p_requestBody,
p_verbose=p_verbose)
if success:
if not responseBody is None:
responseBody = responseBody + nextBody
else:
responseBody = None
return success, errors, responseHeaders, responseBody
# Other functions here...
def main(argv):
arg_apikey = None
arg_orgname = "/all"
arg_file = None
arg_email = None # New email argument
try:
opts, args = getopt.getopt(argv, 'hk:o:f:e:')
except getopt.GetoptError:
print(readMe)
sys.exit()
for opt, arg in opts:
if opt == '-h':
print(readMe)
elif opt == '-k':
arg_apikey = arg
elif opt == '-o':
arg_orgname = arg
elif opt == '-f':
arg_file = arg
elif opt == '-e':
arg_email = arg
# Validate API key
apiKey = getApiKey(arg_apikey)
if apiKey is None:
print("Error: API key missing!")
sys.exit()
filepath = generateFileName() if arg_file is None else arg_file
# Generate CSV logic...
# Send email if email argument provided
if arg_email:
smtp_server = "smtp.office365.com"
port = 587
sender_email = "dwarren@clearlinktech.com"
sender_password = "There is no spoon."
subject = "Meraki Inventory CSV File"
body = f"Attached is the CSV file for {arg_orgname}."
send_email(smtp_server, port, sender_email, sender_password, arg_email, subject, body, filepath) I am curious if I am missing some recent changes or if my rate limiting is incorrect. Thanks, IronBones
... View more