def run(self): """ Method run instructs Cisco DNAC to execute the command set stored in the CommandRunner object. It does not wait for the task to complete on Cisco DNA Center. It does, however, create a new Task object, saves it in the __task attribute, checks the task, and then returns the task's status. See the task.py module for valid task states. When using this function, the programmer must handle task monitoring. Parameters: none Return Values: str: The current state of the command's progress. Usage: d = Dnac() cmd = CommandRunner(d, 'aName') cmdState = cmd.run() """ url = self.dnac.url + self.resource results, status = self.crud.post(url, headers=self.dnac.hdrs, body=self.__cmds, verify=self.verify, timeout=self.timeout) if status != ACCEPTED: raise DnacApiError(MODULE, 'run', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], str(results)) task_id = results['response']['taskId'] self.__task = Task(self.dnac, task_id) return self.__task.check_task()
def commit_template(self, comments='', timeout=5): """ The commit_template updates the parent template to its next version. Once completed, the actual template can be deployed in Cisco DNA Center. :param comments: Comments for the commit action type: str required: no default: '' :return: Template object """ body = {'templateId': self.template_id, 'comments': comments} url = '%s%s%s' % (self.dnac.url, self.resource, TEMPLATE_VERSION_PATH[self.dnac.version]) results, status = self.crud.post(url, headers=self.dnac.hdrs, body=json.dumps(body), verify=self.verify, timeout=timeout) if status != ACCEPTED: raise DnacApiError(MODULE, 'version_template', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], '') # check the tasks' results task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'version_template', TEMPLATE_VERSION_FAILED, '', '', '', '', task.failure_reason) # version succeeded - reload the template and its versions return self.load_template(self.name)
def add_project(self, project): """ Adds a project to Cisco DNA Center :param project: The project data represented as a dict :return: Project object """ # add the project to Cisco DNA Center url = '%s%s' % (self.dnac.url, self.resource) self.__clean_project__(project) body = json.dumps(project) results, status = self.crud.post(url, headers=self.dnac.hdrs, body=body, verify=self.verify, timeout=self.timeout) if status != ACCEPTED: raise DnacApiError( MODULE, 'import_project', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], '' ) # check the tasks' results task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'import_project', PROJECT_IMPORT_FAILED, '', '', '', '', task.failure_reason) # import succeeded; create a new Project object, add it to Dnac and return it return Project(self.dnac, project['name'])
def delete(self): """ Removes the Version object from Cisco DNA Center as well as from the program's Dnac object. :return: None """ url = self.dnac.url + self.resource results, status = self.crud.delete(url, headers=self.dnac.hdrs) if status != OK: raise DnacApiError(MODULE, 'delete', REQUEST_NOT_OK, url, OK, status, ERROR_MSGS[status], '') task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'delete', task.progress, '', '', '', task.failure_reason, '') else: # remove self from Dnac.api{} del self.dnac.api[self.name]
def add_new_template(self, template, project, timeout=5): """ Creates a new template in Cisco DNA Center and assigns it to the project provided. :param template: The template information constructed from a json formatted string type: dict required: yes default: None :param project: A reference to the project to which the template should be assigned type: Project object required: yes default: None :return: Template object """ # ensure the template is correctly formatted self.__is_versioned_template__(template) # prepare the template for import template = self.__prepare_template__(template) # add the template into DNA Center url = '%s%s/%s/template' % (self.dnac.url, PROJECT_RESOURCE_PATH[self.dnac.version], project.project_id) body = json.dumps(template) results, status = self.crud.post(url, headers=self.dnac.hdrs, body=body, verify=self.verify, timeout=timeout) if status != ACCEPTED: if status == _500_: raise DnacApiError(MODULE, 'add_new_template', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], TEMPLATE_ALREADY_EXISTS) else: raise DnacApiError(MODULE, 'add_new_template', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], '') # check the tasks' results task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'add_new_template', TEMPLATE_IMPORT_FAILED, '', '', '', '', task.failure_reason) # import succeeded; reload the project and return the new template project.load_project(project.name) return Template(self.dnac, template['name'])
def run_sync(self, wait=3): """ runSync issues the commands set in the CommandRunner and waits for their completion. It performs this action by creating a Task object and then checks the task's state periodically according to the wait time (seconds) passed as an argument. If no wait time is given, it checks every three seconds. When the task finishes, the Task object will have also loaded the task's results, which can be immediately accessed via the CommandRunner instance (cmd.task.file.results) or from the function's return value (results = cmd.runSync()). Parameters: wait: The time to wait before checking the results. type: int default: 3 required: no Return Values: list: The command set's output Usage: d = Dnac() cmd = CommandRunner(d, 'aCmdName') results = cmd.runSync(wait=10) """ url = self.dnac.url + self.resource results, status = self.crud.post(url, headers=self.dnac.hdrs, body=self.__cmds, verify=self.verify, timeout=self.timeout) if status != ACCEPTED: DnacApiError(MODULE, 'run', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], str(results)) task_id = results['response']['taskId'] self.__task = Task(self.dnac, task_id) self.__task.check_task() while self.__task.progress == TASK_CREATION: time.sleep(wait) self.__task.check_task() return self.__task.file.results
def add_version(self, version, timeout=5): """ Creates a new version of an existing template. :param version: The new version to be added to Cisco DNAC type: dict constructed from a json formatted file required: yes default: None :return: Template object """ # ensure the template is correctly formatted self.__is_versioned_template__(version) # check if the template associated with the new version is already in Dnac if version['name'] not in self.dnac.api: # if not, throw an error raise DnacApiError(MODULE, 'add_version', '%s %s' % (TEMPLATE_NOT_FOUND, version['name']), '', '', '', '', CALL_ADD_NEW_TEMPLATE) else: # if so, save a pointer to it template = self.dnac.api[version['name']] # prepare the new version self.__prepare_version__(version, template) # add the new version to DNAC url = '%s%s' % (self.dnac.url, TEMPLATE_RESOURCE_PATH[self.dnac.version]) body = json.dumps(version) results, status = self.crud.put(url, headers=self.dnac.hdrs, body=body, verify=self.verify, timeout=timeout) if status != ACCEPTED: raise DnacApiError(MODULE, 'add_version', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], '') # check the tasks' results task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'add_version', TEMPLATE_IMPORT_FAILED, '', '', '', '', task.failure_reason) # import succeeded; reload the template return template.load_template(version['name'])
def delete_config_file(self, file_id): """ Removes the specified file, given by its UUID, from Cisco DNAC and from the Version instance. :param file_id: str :return: None """ url = '%s%s/%s/%s' % (self.dnac.url, self.resource, CONFIG_FILE_SUB_RESOURCE_PATH[self.dnac.version], file_id) results, status = self.crud.delete(url, headers=self.dnac.hdrs) if status != OK: raise DnacApiError(MODULE, 'delete_config', REQUEST_NOT_OK, url, OK, status, ERROR_MSGS[status], '') task = Task(self.dnac, results['response']['taskId']) task.get_task_results() if task.is_error: raise DnacApiError(MODULE, 'delete_config', task.progress, '', '', '', task.failure_reason, '') else: for config_file_type, config_file in self.__config_files.items(): if file_id == config_file.id: del self.__config_files[config_file_type] break del self.dnac.api['file_%s' % file_id]
class CommandRunner(DnacApi): """ The CommandRunner class provides the interface for running CLI commands on DNA Center. Note that the command runner API only allows read-only commands, i.e. show commands. Command sets must be formatted as a dictionary with two lists. The first item uses "commands" as its key and then has a list of the actual CLI commands to run. The second value's key is "deviceUuids" and its values are the device IDs where the commands will be run. CommandRunner provides two functions to help produce the dictionary used as the API call's body: formatCmd and formatCmds. To execute the commands, CommandRunner provdes two different methods. run() issues the commands but does not wait for the task to complete. runSync() on the other hand, waits for the task to finish and then collects the results. Attributes: dnac: A pointer to the Dnac object containing the CommandRunner instance. type: Dnac object default: none scope: protected name: A user-friendly name for accessing the CommandRunner object in a Dnac.api{}. type: str default: none scope: protected task: A Task object associated with the commands being run. type: Task object default: none scope: protected cmds: The CLI commands to be run on the target devices. type: dict default: none scope: public resource: The URI for running commands within Cisco DNAC. type: str default: Cisco DNA Center version dependent scope: protected verify: A flag indicating whether or not to verify Cisco DNA Center's certificate. type: bool default: False scope: protected timeout: The number of seconds to wait for Cisco DNAC to respond before timing out. type: int default: 5 scope: protected Usage: d = Dnac() cmds = {'commands': ['show version', 'show module'], 'deviceUuids': ['<switch>', '<router>]} cmd = CommandRunner(d, "aName", cmds=cmds) progress = cmd.run() results = cmd.runSync() """ def __init__(self, dnac, name, cmds=None, verify=False, timeout=5): """ The __init__ method creates a CommandRunner object. As with all classes that inherit from DnacApi, a minimum of a Dnac container and a name must be given. Optionally, a dictionary of the CLI commands to run and the UUIDs of devices to run them on may be specified. Parameters: dnac: A reference to the containing Dnac object. type: Dnac object default: none required: yes name: A user friendly name for finding this object in a Dnac instance. type: str default: none required: yes cmds: A dict with the commands and target devices. type: dict default: none required: no verify: A flag used to check Cisco DNAC's certificate. type: boolean default: False required: no timeout: The number of seconds to wait for Cisco DNAC's response. type: int default: 5 required: no Return Values: CommandRunner object: The newly constructed CommandRunner Usage: d = Dnac() cmds = {'commands': ['show version', 'show module'], 'deviceUuids': ['<switch>', '<router>]} cmd = CommandRunner(d, "aName", cmds=cmds) """ # check Cisco DNA Center's version and set the resourece path if cmds is None: cmds = {} if dnac.version in SUPPORTED_DNAC_VERSIONS: path = COMMANDRUNNER_RESOURCE_PATH[dnac.version] else: raise DnacError('__init__: %s: %s' % (UNSUPPORTED_DNAC_VERSION, dnac.version)) # setup the attributes self.__cmds = cmds # commands to run self.__task = None # Task object created after running cmds super(CommandRunner, self).__init__(dnac, name, resource=path, verify=verify, timeout=timeout) # end __init__() @property def cmds(self): """ Get method cmds returns the __cmds body to be sent to Cisco DNAC. Parameters: none Return Values: dict: A list of CLI commands and the devices on which to execute them. Usage: d = Dnac() cmds = {'commands': ['show version', 'show module'], 'deviceUuids': ['<switch>', '<router>]} cmd = CommandRunner(d, "aName", cmds=cmds) pprint.PrettyPrint(cmd.cmds) """ return self.__cmds # end cmds getter @cmds.setter def cmds(self, cmds): """ Method cmds sets its __cmds attribute to the dictionary of commands given. Parameters: cmds: A dict of commands and device UUIDs to run the commands against. type: dict default: None required: Yes Return Values: None Usage: d = Dnac() cmd = CommandRunner(d, 'aName') cmds = ['show version', 'show module', 'show proc cpu'] cmd.cmds = cmds """ self.__cmds = cmds # end cmds setter @property def task(self): """ The task get function returns the Task object stored in __task. Parameters: none Return Values: Task object: The task associated with the CommandRunner instance. Usage: d = Dnac() cmd = CommandRunner(d, 'aName') cliCmd = 'show module' switch = '<switch's UUID>' cmd.formatCmd(cliCmd, switch) cmd.run() task = cmd.task """ return self.__task # end cmds getter def format_cmd(self, cmd, uuid): """ The formatCmd method takes a single CLI command and runs it against the UUID of a network device in Cisco DNA Center. It converts the command and the UUID into a dict stored in the __cmds attribute and returns __cmds' value. Parameters: cmd: A CLI command. type: str default: none required: yes uuid: A network device UUID. type: str default: none required: yes Return Values: dict: The command instructions used as the body for making an API call to Cisco DNAC's command runner. Usage: d = Dnac() cmd = 'show version' uuid = '<switch_uuid>' cmd = CommandRunner(d, 'aName') cmd.format_cmd(cmd, uuid) """ c = [cmd] u = [uuid] cmds = {'commands': c, 'deviceUuids': u} self.__cmds = json.dumps(cmds) return self.__cmds # end format_cmd() def format_cmds(self, cmd_list, uuid_list): """ The format_cmds method accepts a list of CLI commands to run against a list of UUIDs for the target network devices in Cisco DNA Center. It converts the two lists into a dict stored in the __cmds attribute and returns __cmds' value. Parameters: cmd_list: A list of CLI commands. type: list of str default: none required: yes uuid_list: A list of network device UUIDs. type: list of str default: none required: yes Return Values: dict: The command instructions used as the body for making an API call to Cisco DNAC's command runner. Usage: d = Dnac() cmds = ['show version', 'show ip interface brief'] uuids = ['<switch_uuid>', '<router_uuid>'] cmds = CommandRunner(d, 'aName') cmds.format_cmds(cmds, uuids) """ cmds = {'commands': cmd_list, 'deviceUuids': uuid_list} self.__cmds = json.dumps(cmds) return self.__cmds # end format_cmds() def run(self): """ Method run instructs Cisco DNAC to execute the command set stored in the CommandRunner object. It does not wait for the task to complete on Cisco DNA Center. It does, however, create a new Task object, saves it in the __task attribute, checks the task, and then returns the task's status. See the task.py module for valid task states. When using this function, the programmer must handle task monitoring. Parameters: none Return Values: str: The current state of the command's progress. Usage: d = Dnac() cmd = CommandRunner(d, 'aName') cmdState = cmd.run() """ url = self.dnac.url + self.resource results, status = self.crud.post(url, headers=self.dnac.hdrs, body=self.__cmds, verify=self.verify, timeout=self.timeout) if status != ACCEPTED: raise DnacApiError(MODULE, 'run', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], str(results)) task_id = results['response']['taskId'] self.__task = Task(self.dnac, task_id) return self.__task.check_task() # end run() def run_sync(self, wait=3): """ runSync issues the commands set in the CommandRunner and waits for their completion. It performs this action by creating a Task object and then checks the task's state periodically according to the wait time (seconds) passed as an argument. If no wait time is given, it checks every three seconds. When the task finishes, the Task object will have also loaded the task's results, which can be immediately accessed via the CommandRunner instance (cmd.task.file.results) or from the function's return value (results = cmd.runSync()). Parameters: wait: The time to wait before checking the results. type: int default: 3 required: no Return Values: list: The command set's output Usage: d = Dnac() cmd = CommandRunner(d, 'aCmdName') results = cmd.runSync(wait=10) """ url = self.dnac.url + self.resource results, status = self.crud.post(url, headers=self.dnac.hdrs, body=self.__cmds, verify=self.verify, timeout=self.timeout) if status != ACCEPTED: DnacApiError(MODULE, 'run', REQUEST_NOT_ACCEPTED, url, ACCEPTED, status, ERROR_MSGS[status], str(results)) task_id = results['response']['taskId'] self.__task = Task(self.dnac, task_id) self.__task.check_task() while self.__task.progress == TASK_CREATION: time.sleep(wait) self.__task.check_task() return self.__task.file.results