From 9536920cd90414b5fbf2f927142f3700d025578e Mon Sep 17 00:00:00 2001 From: Vijayakumar Perumal Date: Fri, 9 Aug 2024 18:00:53 +0530 Subject: [PATCH 1/8] As value for domain in login payload can change for remote authentication made domain optional in connections so that when domain is not there in testbed it defaults to DefaultAuth. And if any new domain is added then value can be specified in testbed.yaml --- src/rest/connector/libs/nd/implementation.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index b75bf26..791a394 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -32,7 +32,7 @@ class Implementation(Imp): credentials: rest: username: admin - password: cisco123 + password: [REDACT] Code Example ------------ @@ -77,7 +77,9 @@ def connect(self, timeout=30, retries=3, retry_wait=10): credentials: rest: username: admin - password: cisco123 + password: [REDACT] + domain: 'DefaultAuth' # this is optional if domain is not specified 'DefaultAuth' becomes default value + Code Example ------------ @@ -102,11 +104,12 @@ def connect(self, timeout=30, retries=3, retry_wait=10): login_url = '{f}login'.format(f=self.url) username, password = get_username_password(self) + domain = str(self.connection_info['credentials']['rest'].get('domain','DefaultAuth')) payload = { "userName": username, "userPasswd": password, - "domain": "DefaultAuth" + "domain": domain } headers = { @@ -224,12 +227,12 @@ def get(self, api_url, expected_status_code=requests.codes.ok, tries=retries)) try: - output = response.json() + output = response.json() # Response need not be always in json + log.info("Output received:\n{output}".format(output= + json.dumps(output, indent=2, sort_keys=True))) except Exception: output = response.text - - log.info("Output received:\n{output}".format(output= - json.dumps(output, indent=2, sort_keys=True))) + log.info(f"Output received: {output}") # Make sure it returned requests.codes.ok if response.status_code != expected_status_code: @@ -413,7 +416,7 @@ def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, @BaseConnection.locked @isconnected def delete(self, api_url, expected_status_code=requests.codes.ok, - timeout=30, retries=3, retry_wait=10): + content_type='json',timeout=30, retries=3, retry_wait=10): """DELETE REST Command to delete information from the device Arguments --------- From ac5e5fd1c9c895d23b3abe14433f3e1207f632b2 Mon Sep 17 00:00:00 2001 From: Vijayakumar Perumal Date: Fri, 9 Aug 2024 20:26:08 +0530 Subject: [PATCH 2/8] Added params, data, json, files in get, post, put, and delete Overloaded get, post, put and delete so that they are backward compatible. --- src/rest/connector/libs/nd/implementation.py | 365 +++++++++++++------ 1 file changed, 250 insertions(+), 115 deletions(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index 791a394..f421576 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -45,13 +45,14 @@ class Implementation(Imp): """ @BaseConnection.locked - def connect(self, timeout=30, retries=3, retry_wait=10): + def connect(self, timeout=30, retries=3, retry_wait=10, verify=False): """connect to the device via REST Arguments --------- timeout (int): Timeout value retries (int): Max retries on request exception (default: 3) retry_wait (int): Seconds to wait before retry (default: 10) + verify (bool): defaults to False Raises ------ @@ -98,10 +99,11 @@ def connect(self, timeout=30, retries=3, retry_wait=10): ip = self.connection_info['ip'].exploded if 'port' in self.connection_info: port = self.connection_info['port'] - self.url = 'https://{ip}:{port}/'.format(ip=ip, port=port) + self.url = f'https://{ip}:{port}/' else: - self.url = 'https://{ip}/'.format(ip=ip) - login_url = '{f}login'.format(f=self.url) + self.url = f'https://{ip}/' + self.verify = verify + login_url = f'{self.url}login' username, password = get_username_password(self) domain = str(self.connection_info['credentials']['rest'].get('domain','DefaultAuth')) @@ -132,34 +134,32 @@ def connect(self, timeout=30, retries=3, retry_wait=10): # Make sure it returned requests.codes.ok if response.status_code != requests.codes.ok: # Something bad happened - raise RequestException("Connection to '{ip}' has returned the " - "following code '{c}', instead of the " - "expected status code '{ok}'" - .format(ip=ip, c=response.status_code, - ok=requests.codes.ok)) + raise RequestException(f"Connection to '{ip}' has returned the " + f"following code '{response.status_code}', instead of the " + f"expected status code '{requests.codes.ok}'") break except Exception: - log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format( - self.device.name, retry_wait), exc_info=True) + log.warning(f'Request to {self.device.name} failed. Waiting {retry_wait} seconds before retrying\n', + exc_info=True) time.sleep(retry_wait) else: - raise ConnectionError('Connection to {} failed'.format(self.device.name)) + raise ConnectionError(f'Connection to {self.device.name} failed') self._is_connected = True - log.info("Connected successfully to '{d}'".format(d=self.device.name)) + log.info(f"Connected successfully to '{self.device.name}'") @BaseConnection.locked def disconnect(self): """disconnect the device for this particular alias""" - log.info("Disconnecting from '{d}' with " - "alias '{a}'".format(d=self.device.name, a=self.alias)) + log.info(f"Disconnecting from '{self.device.name}' with " + f"alias '{self.alias}'") try: self.session.close() finally: self._is_connected = False - log.info("Disconnected successfully from " - "'{d}'".format(d=self.device.name)) + log.info(f"Disconnected successfully from " + "'{self.device.name}'") def isconnected(func): """Decorator to make sure session to device is active @@ -188,7 +188,7 @@ def decorated(self, *args, **kwargs): @BaseConnection.locked @isconnected - def get(self, api_url, expected_status_code=requests.codes.ok, + def get(self, api_url, params=None, expected_status_code=requests.codes.ok, timeout=30, retries=3, retry_wait=10): """GET REST Command to retrieve information from the device Arguments @@ -201,35 +201,35 @@ def get(self, api_url, expected_status_code=requests.codes.ok, """ if not self.connected: - raise Exception("'{d}' is not connected for " - "alias '{a}'".format(d=self.device.name, - a=self.alias)) + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") #Eliminate the starting "/" if present, as it may cause problems api_url = api_url.lstrip('/') # Deal with the url - full_url = "{f}{api_url}".format(f=self.url, api_url=api_url) + full_url = f"{self.url}{api_url}" - log.info("Sending GET command to '{d}':" \ - "\nURL: {furl}".format(d=self.device.name, furl=full_url)) + log.info(f"Sending GET command to '{self.device.name}':" \ + f"\nURL: {full_url}") for _ in range(retries): try: - response = self.session.get(full_url, timeout=timeout, verify=False) + if params: + response = self.session.get(full_url, params=params, timeout=timeout, verify=self.verify) + else: + response = self.session.get(full_url, timeout=timeout, verify=self.verify) break except Exception: - log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format( - self.device.name, retry_wait), exc_info=True) + log.warning(f'Request to {self.device.name} failed. ' + f'Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) time.sleep(retry_wait) else: - raise RequestException('Sending "{furl}" to "{d}" has failed ' - 'after {tries} tries'.format(furl=full_url, - d=self.device.name, - tries=retries)) + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') try: output = response.json() # Response need not be always in json - log.info("Output received:\n{output}".format(output= - json.dumps(output, indent=2, sort_keys=True))) + log.info(f"Output received:\n{json.dumps(output, indent=2, sort_keys=True)}") except Exception: output = response.text log.info(f"Output received: {output}") @@ -237,14 +237,9 @@ def get(self, api_url, expected_status_code=requests.codes.ok, # Make sure it returned requests.codes.ok if response.status_code != expected_status_code: # Something bad happened - raise RequestException("GET {furl} to {d} has returned the " - "following code '{c}', instead of the " - "expected status code '{e}'" - ", got:\n {msg}".format(furl=full_url, - d=self.device.name, - c=response.status_code, - e=expected_status_code, - msg=response.text)) + raise RequestException(f"GET {full_url} to {self.device.name} has returned the following code " + f"{response.status_code}, instead of the expected status code " + f"'{expected_status_code}', got {response.text}") return output @BaseConnection.locked @@ -262,18 +257,15 @@ def post(self, api_url, payload, expected_status_code=requests.codes.ok, """ if not self.connected: - raise Exception("'{d}' is not connected for " - "alias '{a}'".format(d=self.device.name, - a=self.alias)) + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") # Eliminate the starting "/" if present, as it may cause problems api_url = api_url.lstrip('/') # Deal with the url - full_url = '{f}{api_url}'.format(f=self.url, api_url=api_url) + full_url = f'{self.url}{api_url}' - log.info("Sending POST command to '{d}':" \ - "\nURL: {furl}\nPayload:{p}".format(d=self.device.name, - furl=full_url, - p=payload)) + log.info(f"Sending POST command to '{self.device.name}':" \ + f"\nURL: {full_url}\nPayload:{payload}") headers = {'form': 'application/x-www-form-urlencoded', 'json': 'application/json', 'xml': 'application/xml'} @@ -282,10 +274,9 @@ def post(self, api_url, payload, expected_status_code=requests.codes.ok, payload = urllib.parse.urlencode(payload, safe=':!') elif content_type == 'xml': if isinstance(payload, dict): - raise ValueError("Error on {d} during POST command: " + raise ValueError(f"Error on {self.device.name} during POST command: " "Payload needs to be string in xml format if used " - "in conjunction with content_type='xml' argument" - .format(d=self.device.name)) + "in conjunction with content_type='xml' argument") for _ in range(retries): try: @@ -300,19 +291,17 @@ def post(self, api_url, payload, expected_status_code=requests.codes.ok, headers['json'])}) break except Exception: - log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format( - self.device.name, retry_wait), exc_info=True) + log.warning(f'Request to {self.device.name} failed. Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) time.sleep(retry_wait) else: - raise RequestException('Sending "{furl}" to "{d}" has failed ' - 'after {tries} tries'.format(furl=full_url, - d=self.device.name, - tries=retries)) + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') try: # response might not pe in JSON format output = response.json() - log.info("Output received:\n{output}".format(output=output)) + log.info(f"Output received:\n{output}") except Exception: output = response.content if content_type == 'xml' else response.text log.info(f"'Post' operation did not return a json response: {output}") @@ -320,16 +309,68 @@ def post(self, api_url, payload, expected_status_code=requests.codes.ok, # Make sure it returned requests.codes.ok if response.status_code != expected_status_code: # Something bad happened - raise RequestException("POST {furl} to {d} has returned the " - "following code '{c}', instead of the " - "expected status code '{e}'" - ", got:\n {msg}".format(furl=full_url, - d=self.device.name, - c=response.status_code, - e=expected_status_code, - msg=response.text)) + raise RequestException(f"POST {full_url} to {self.device.name} has returned the " + f"following code '{response.status_code}', instead of the " + f"expected status code '{expected_status_code}'" + f", got:\n {response.text}") return output + @BaseConnection.locked + @isconnected + def post(self, api_url, params=None, data=None, json=None, files=None, + timeout=30, retries=3, retry_wait=10) -> requests.Response: + """POST REST Command to configure information from the device + Arguments + --------- + api_url (string): subdirectory part of the API URL + params (dict): Query string parameters + data (dict): + json (json) : if request header Content-Type is application/json + files (dict): + expected_status_code (int): Expected result + timeout (int): Maximum time + """ + + if not self.connected: + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") + # Eliminate the starting "/" if present, as it may cause problems + api_url = api_url.lstrip('/') + # Deal with the url + full_url = f'{self.url}{api_url}' + + for _ in range(retries): + try: + if params and not json: + response = self.session.post(full_url, params=params, timeout=timeout, + verify=self.verify) + elif params and json: + response = self.session.post(full_url,params=params, json=json, + timeout=timeout, verify=self.verify) + elif json and not params: + response = self.session.post(full_url,json=json, + timeout=timeout, verify=self.verify) + elif data: + response = self.session.post(full_url, data=data, + timeout=timeout, verify=self.verify) + elif files: + response = self.session.post(full_url, files=files, + timeout=timeout, verify=self.verify) + else: + response = self.session.post(full_url, timeout=timeout, verify=self.verify) + + break + except Exception: + log.warning(f'Request to {self.device.name} failed. ' + f'Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) + time.sleep(retry_wait) + else: + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') + + return response + @BaseConnection.locked @isconnected def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, @@ -345,18 +386,15 @@ def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, """ if not self.connected: - raise Exception("'{d}' is not connected for " - "alias '{a}'".format(d=self.device.name, - a=self.alias)) + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") # Eliminate the starting "/" if present, as it may cause problems api_url = api_url.lstrip('/') # Deal with the url - full_url = '{f}{api_url}'.format(f=self.url, api_url=api_url) + full_url = f'{self.url}{api_url}' - log.info("Sending PUT command to '{d}':" \ - "\nURL: {furl}\nPayload:{p}".format(d=self.device.name, - furl=full_url, - p=payload)) + log.info(f"Sending PUT command to '{self.device.name}':" \ + f"\nURL: {full_url}\nPayload:{payload}") headers = {'form': 'application/x-www-form-urlencoded', 'json': 'application/json', 'xml': 'application/xml'} @@ -365,10 +403,9 @@ def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, payload = urllib.parse.urlencode(payload, safe=':!') elif content_type == 'xml': if isinstance(payload, dict): - raise ValueError("Error on {d} during PUT command: " - "Payload must to be string in xml format if used " - "in conjunction with content_type='xml' argument" - .format(d=self.device.name)) + raise ValueError(f"Error on {self.device.name} during PUT command: " + f"Payload must to be string in xml format if used " + f"in conjunction with content_type='xml' argument") for _ in range(retries): try: @@ -383,19 +420,17 @@ def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, headers['json'])}) break except Exception: - log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format( - self.device.name, retry_wait), exc_info=True) + log.warning(f'Request to {self.device.name} failed. Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) time.sleep(retry_wait) else: - raise RequestException('Sending "{furl}" to "{d}" has failed ' - 'after {tries} tries'.format(furl=full_url, - d=self.device.name, - tries=retries)) + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') try: # response might not pe in JSON format output = response.json() - log.info("Output received:\n{output}".format(output=output)) + log.info(f"Output received:\n{output}") except Exception: output = response.content if content_type == 'xml' else response.text log.info(f"'Put' operation did not return a json response: {output}") @@ -403,16 +438,68 @@ def put(self, api_url, payload=None, expected_status_code=requests.codes.ok, # Make sure it returned requests.codes.ok if response.status_code != expected_status_code: # Something bad happened - raise RequestException("PUT {furl} to {d} has returned the " - "following code '{c}', instead of the " - "expected status code '{e}'" - ", got:\n {msg}".format(furl=full_url, - d=self.device.name, - c=response.status_code, - e=expected_status_code, - msg=response.text)) + raise RequestException(f"PUT {full_url} to {self.device.name} has returned the " + f"following code '{response.status_code}', instead of the " + f"expected status code '{expected_status_code}'" + f", got:\n {response.text}") return output + @BaseConnection.locked + @isconnected + def put(self, api_url, params=None, data=None, json=None, files=None, + timeout=30, retries=3, retry_wait=10) -> requests.Response: + """PUT REST Command to configure information from the device + Arguments + --------- + api_url (string): subdirectory part of the API URL + params (dict): Query string parameters + data (dict): + json (json) : if request header Content-Type is application/json + files (dict): + expected_status_code (int): Expected result + timeout (int): Maximum time + """ + + if not self.connected: + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") + # Eliminate the starting "/" if present, as it may cause problems + api_url = api_url.lstrip('/') + # Deal with the url + full_url = f'{self.url}{api_url}' + + for _ in range(retries): + try: + if params and not json: + response = self.session.put(full_url, params=params, timeout=timeout, + verify=self.verify) + elif params and json: + response = self.session.put(full_url, params=params, json=json, + timeout=timeout, verify=self.verify) + elif json and not params: + response = self.session.put(full_url, json=json, + timeout=timeout, verify=self.verify) + elif data: + response = self.session.put(full_url, data=data, + timeout=timeout, verify=self.verify) + elif files: + response = self.session.put(full_url, files=files, + timeout=timeout, verify=self.verify) + else: + response = self.session.put(full_url, timeout=timeout, verify=self.verify) + + break + except Exception: + log.warning(f'Request to {self.device.name} failed. ' + f'Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) + time.sleep(retry_wait) + else: + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') + + return response + @BaseConnection.locked @isconnected def delete(self, api_url, expected_status_code=requests.codes.ok, @@ -426,16 +513,15 @@ def delete(self, api_url, expected_status_code=requests.codes.ok, timeout (int): Maximum time """ if not self.connected: - raise Exception("'{d}' is not connected for " - "alias '{a}'".format(d=self.device.name, - a=self.alias)) + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") # Eliminate the starting "/" if present, as it may cause problems api_url = api_url.lstrip('/') # Deal with the url - full_url = '{f}{api_url}'.format(f=self.url, api_url=api_url) + full_url = f'{self.url}{api_url}' - log.info("Sending DELETE command to '{d}':" \ - "\nURL: {furl}".format(d=self.device.name, furl=full_url)) + log.info(f"Sending DELETE command to '{self.device.name}':" \ + f"\nURL: {full_url}") for i in range(retries): try: @@ -443,19 +529,16 @@ def delete(self, api_url, expected_status_code=requests.codes.ok, response = self.session.delete(full_url, timeout=timeout, verify=False) break except Exception: - log.warning('Request to {} failed. Waiting {} seconds before retrying\n'.format( - self.device.name, retry_wait), exc_info=True) + log.warning(f'Request to {self.device.name} failed. Waiting {retry_wait} seconds before retrying\n', exc_info=True) time.sleep(retry_wait) else: - raise RequestException('Sending "{furl}" to "{d}" has failed ' - 'after {tries} tries'.format(furl=full_url, - d=self.device.name, - tries=retries)) + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') try: # response might not pe in JSON format output = response.json() - log.info("Output received:\n{output}".format(output=output)) + log.info(f"Output received:\n{output}") except ValueError: output = response.text log.info(f"'Delete' operation did not return a json response: {output}") @@ -463,12 +546,64 @@ def delete(self, api_url, expected_status_code=requests.codes.ok, # Make sure it returned requests.codes.ok if response.status_code != expected_status_code: # Something bad happened - raise RequestException("DELETE {furl} to {d} has returned the " - "following code '{c}', instead of the " - "expected status code '{e}'" - ", got:\n {msg}".format(furl=full_url, - d=self.device.name, - c=response.status_code, - e=expected_status_code, - msg=response.text)) + raise RequestException(f"DELETE {full_url} to {self.device.name} has returned the " + f"following code '{response.status_code}', instead of the " + f"expected status code '{expected_status_code}'" + f", got:\n {response.text}") return output + + @BaseConnection.locked + @isconnected + def delete(self, api_url, params=None, data=None, json=None, files=None, + timeout=30, retries=3, retry_wait=10) -> requests.Response: + """POST REST Command to configure information from the device + Arguments + --------- + api_url (string): subdirectory part of the API URL + params (dict): Query string parameters + data (dict): + json (json) : if request header Content-Type is application/json + files (dict): + timeout (int): Maximum time + """ + + if not self.connected: + raise Exception(f"'{self.device.name}' is not connected for " + f"alias '{self.alias}'") + # Eliminate the starting "/" if present, as it may cause problems + api_url = api_url.lstrip('/') + # Deal with the url + full_url = f'{self.url}{api_url}' + + for _ in range(retries): + try: + if params and not json: + response = self.session.delete(full_url, params=params, timeout=timeout, + verify=self.verify) + elif params and json: + response = self.session.delete(full_url, params=params, json=json, + timeout=timeout, verify=self.verify) + elif json and not params: + response = self.session.delete(full_url, json=json, + timeout=timeout, verify=self.verify) + elif data: + response = self.session.delete(full_url, data=data, + timeout=timeout, verify=self.verify) + elif files: + response = self.session.delete(full_url, files=files, + timeout=timeout, verify=self.verify) + else: + response = self.session.delete(full_url, timeout=timeout, verify=self.verify) + + break + except Exception: + log.warning(f'Request to {self.device.name} failed. ' + f'Waiting {retry_wait} seconds before retrying\n' + , exc_info=True) + time.sleep(retry_wait) + else: + raise RequestException(f'Sending "{full_url}" to "{self.device.name}" has failed ' + f'after {retries} tries') + + return response + From 87692c8952a2db786b11ceca7e30b8e490bda990 Mon Sep 17 00:00:00 2001 From: vijperum <82870696+vijperum@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:17:06 +0530 Subject: [PATCH 3/8] Update src/rest/connector/libs/nd/implementation.py Co-authored-by: Thomas Ryan --- src/rest/connector/libs/nd/implementation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index f421576..d212fe4 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -503,7 +503,7 @@ def put(self, api_url, params=None, data=None, json=None, files=None, @BaseConnection.locked @isconnected def delete(self, api_url, expected_status_code=requests.codes.ok, - content_type='json',timeout=30, retries=3, retry_wait=10): + timeout=30, retries=3, retry_wait=10, content_type='json'): """DELETE REST Command to delete information from the device Arguments --------- From fcdf6f40c577e67634c4529094fe352a59a14c87 Mon Sep 17 00:00:00 2001 From: vijperum <82870696+vijperum@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:18:41 +0530 Subject: [PATCH 4/8] Update src/rest/connector/libs/nd/implementation.py Co-authored-by: Thomas Ryan --- src/rest/connector/libs/nd/implementation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index d212fe4..e4a4d82 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -188,8 +188,8 @@ def decorated(self, *args, **kwargs): @BaseConnection.locked @isconnected - def get(self, api_url, params=None, expected_status_code=requests.codes.ok, - timeout=30, retries=3, retry_wait=10): + def get(self, api_url, expected_status_code=requests.codes.ok, + timeout=30, retries=3, retry_wait=10, params=None): """GET REST Command to retrieve information from the device Arguments --------- From 24ceb716eef8001890de7fe452b272ed06b7a524 Mon Sep 17 00:00:00 2001 From: vijperum <82870696+vijperum@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:25:07 +0530 Subject: [PATCH 5/8] Update implementation.py Updated doc for verify --- src/rest/connector/libs/nd/implementation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index e4a4d82..63afb31 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -52,7 +52,7 @@ def connect(self, timeout=30, retries=3, retry_wait=10, verify=False): timeout (int): Timeout value retries (int): Max retries on request exception (default: 3) retry_wait (int): Seconds to wait before retry (default: 10) - verify (bool): defaults to False + verify (bool): ignore SSL certificates (default:False) Raises ------ From ec910d10995ad29c22b9f7ca9b5997e15c4437ba Mon Sep 17 00:00:00 2001 From: Vijayakumar Perumal Date: Fri, 23 Aug 2024 20:41:42 +0530 Subject: [PATCH 6/8] Addressed Add defaults to docstring Addressed Are we not able to pass None to the values? IE, surely if json is None we can still do self.session.post(..., json=None)? Would be better than having this large if/else structure --- src/rest/connector/libs/nd/implementation.py | 78 +++++--------------- 1 file changed, 18 insertions(+), 60 deletions(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index 63afb31..f36de2b 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -324,9 +324,9 @@ def post(self, api_url, params=None, data=None, json=None, files=None, --------- api_url (string): subdirectory part of the API URL params (dict): Query string parameters - data (dict): + data (dict): if request header Content-Type is multipart/form-data json (json) : if request header Content-Type is application/json - files (dict): + files (dict): if request header Content-Type is application/octet-stream expected_status_code (int): Expected result timeout (int): Maximum time """ @@ -341,24 +341,10 @@ def post(self, api_url, params=None, data=None, json=None, files=None, for _ in range(retries): try: - if params and not json: - response = self.session.post(full_url, params=params, timeout=timeout, - verify=self.verify) - elif params and json: - response = self.session.post(full_url,params=params, json=json, - timeout=timeout, verify=self.verify) - elif json and not params: - response = self.session.post(full_url,json=json, - timeout=timeout, verify=self.verify) - elif data: - response = self.session.post(full_url, data=data, - timeout=timeout, verify=self.verify) - elif files: - response = self.session.post(full_url, files=files, - timeout=timeout, verify=self.verify) - else: - response = self.session.post(full_url, timeout=timeout, verify=self.verify) - + response = self.session.post(full_url, params=params, + data=data, json=json, + files=files, timeout=timeout, + verify=self.verify) break except Exception: log.warning(f'Request to {self.device.name} failed. ' @@ -453,9 +439,9 @@ def put(self, api_url, params=None, data=None, json=None, files=None, --------- api_url (string): subdirectory part of the API URL params (dict): Query string parameters - data (dict): + data (dict): if request header Content-Type is multipart/form-data json (json) : if request header Content-Type is application/json - files (dict): + files (dict): if request header Content-Type is application/octet-stream expected_status_code (int): Expected result timeout (int): Maximum time """ @@ -470,24 +456,10 @@ def put(self, api_url, params=None, data=None, json=None, files=None, for _ in range(retries): try: - if params and not json: - response = self.session.put(full_url, params=params, timeout=timeout, - verify=self.verify) - elif params and json: - response = self.session.put(full_url, params=params, json=json, - timeout=timeout, verify=self.verify) - elif json and not params: - response = self.session.put(full_url, json=json, - timeout=timeout, verify=self.verify) - elif data: - response = self.session.put(full_url, data=data, - timeout=timeout, verify=self.verify) - elif files: - response = self.session.put(full_url, files=files, - timeout=timeout, verify=self.verify) - else: - response = self.session.put(full_url, timeout=timeout, verify=self.verify) - + response = self.session.post(full_url, params=params, + data=data, json=json, + files=files, timeout=timeout, + verify=self.verify) break except Exception: log.warning(f'Request to {self.device.name} failed. ' @@ -561,9 +533,9 @@ def delete(self, api_url, params=None, data=None, json=None, files=None, --------- api_url (string): subdirectory part of the API URL params (dict): Query string parameters - data (dict): + data (dict): if request header Content-Type is multipart/form-data json (json) : if request header Content-Type is application/json - files (dict): + files (dict): if request header Content-Type is application/octet-stream timeout (int): Maximum time """ @@ -577,24 +549,10 @@ def delete(self, api_url, params=None, data=None, json=None, files=None, for _ in range(retries): try: - if params and not json: - response = self.session.delete(full_url, params=params, timeout=timeout, - verify=self.verify) - elif params and json: - response = self.session.delete(full_url, params=params, json=json, - timeout=timeout, verify=self.verify) - elif json and not params: - response = self.session.delete(full_url, json=json, - timeout=timeout, verify=self.verify) - elif data: - response = self.session.delete(full_url, data=data, - timeout=timeout, verify=self.verify) - elif files: - response = self.session.delete(full_url, files=files, - timeout=timeout, verify=self.verify) - else: - response = self.session.delete(full_url, timeout=timeout, verify=self.verify) - + response = self.session.post(full_url, params=params, + data=data, json=json, + files=files, timeout=timeout, + verify=self.verify) break except Exception: log.warning(f'Request to {self.device.name} failed. ' From e43c58eb451a8819733c75f73f6df7bc57b40fc0 Mon Sep 17 00:00:00 2001 From: vijperum <82870696+vijperum@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:54:32 +0530 Subject: [PATCH 7/8] Update src/rest/connector/libs/nd/implementation.py Co-authored-by: Thomas Ryan --- src/rest/connector/libs/nd/implementation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index f36de2b..b1b8287 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -32,7 +32,7 @@ class Implementation(Imp): credentials: rest: username: admin - password: [REDACT] + password: cisco123 Code Example ------------ From cea68ca0fceb6bb605a1d1591d1d258cb6229984 Mon Sep 17 00:00:00 2001 From: Vijayakumar Perumal <82870696+vijperum@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:34:08 +0530 Subject: [PATCH 8/8] Update src/rest/connector/libs/nd/implementation.py Co-authored-by: Thomas Ryan --- src/rest/connector/libs/nd/implementation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rest/connector/libs/nd/implementation.py b/src/rest/connector/libs/nd/implementation.py index b1b8287..e9f5826 100644 --- a/src/rest/connector/libs/nd/implementation.py +++ b/src/rest/connector/libs/nd/implementation.py @@ -78,7 +78,7 @@ def connect(self, timeout=30, retries=3, retry_wait=10, verify=False): credentials: rest: username: admin - password: [REDACT] + password: cisco123 domain: 'DefaultAuth' # this is optional if domain is not specified 'DefaultAuth' becomes default value