def get_path(self, path=''): if not path: host = get_attribute_by_name('Backup Location') if ':' not in host: scheme = get_attribute_by_name('Backup Type') scheme = re.sub('(:|/+).*$', '', scheme, re.DOTALL) host = re.sub('^/+', '', host) host = '{}://{}'.format(scheme, host) path = host url = UrlParser.parse_url(path) if UrlParser.SCHEME not in url or not url[UrlParser.SCHEME]: raise Exception('ConfigurationOperations', "Backup Type is wrong or empty") if url[UrlParser.SCHEME].lower() in self.AUTHORIZATION_REQUIRED_STORAGES: if UrlParser.USERNAME not in url or not url[UrlParser.USERNAME]: url[UrlParser.USERNAME] = get_attribute_by_name('Backup User') if UrlParser.PASSWORD not in url or not url[UrlParser.PASSWORD]: url[UrlParser.PASSWORD] = decrypt_password(get_attribute_by_name('Backup Password')) try: result = UrlParser.build_url(url) except Exception as e: self.logger.error('Failed to build url: {}'.format(e)) raise Exception('ConfigurationOperations', 'Failed to build path url to remote host') return result
def save(self, folder_path=None, configuration_type='running', vrf_management_name=None): """Backup 'startup-config' or 'running-config' from device to provided file_system [ftp|tftp] Also possible to backup config to localhost :param folder_path: tftp/ftp server where file be saved :param configuration_type: what file to backup :return: status message / exception """ expected_map = dict() full_path = self.get_path(folder_path) url = UrlParser.parse_url(full_path) password = url.get(UrlParser.PASSWORD) if password: expected_map = {r'[Pp]assword\s*:': lambda session: session.send_line(password)} url.pop(UrlParser.PASSWORD) url[UrlParser.NETLOC] = url[UrlParser.NETLOC].replace(':{}'.format(password), '') if not configuration_type: configuration_type = 'running' if not re.search('startup|running', configuration_type, re.IGNORECASE): raise Exception('EricssonConfigurationOperations', "Source filename must be 'Running' or" + " 'Startup'!") system_name = re.sub('\s+', '_', self.resource_name) if len(system_name) > 23: system_name = system_name[:23] destination_filename = '{0}-{1}-{2}'.format(system_name, configuration_type.lower(), _get_time_stamp()) self.logger.info('destination filename is {0}'.format(destination_filename)) url[UrlParser.FILENAME] = destination_filename destination_file_path = UrlParser.build_url(url) expected_map['overwrite'] = lambda session: session.send_line('y') if 'startup' in configuration_type.lower(): # startup_config_file = self.cli.send_command('show configuration | include boot') # match_startup_config_file = re.search('\w+\.\w+', startup_config_file) # if not match_startup_config_file: # raise Exception('EricssonConfigurationOperations', 'no startup/boot configuration found') # startup_config = match_startup_config_file.group() # command = 'copy {0} {1}'.format(startup_config, destination_file) raise Exception('EricssonConfigurationOperations', 'There is no startup configuration for {0}'.format(self.resource_name)) else: command = 'save configuration {0}'.format(destination_file_path) output = self.cli.send_command(command, expected_map=expected_map) is_downloaded = self._check_download_from_tftp(output) if is_downloaded[0]: self.logger.info('Save configuration completed.') return destination_filename else: self.logger.info('Save configuration failed with errors: {0}'.format(is_downloaded[1])) raise Exception('EricssonConfigurationOperations', 'Save configuration failed with errors:', is_downloaded[1])
def test_url_join(self): correct_url = 'ftp://*****:*****@google.com/folder1/file2' url = '/folder1/file2' parsed_url = UrlParser.parse_url(url) parsed_url[UrlParser.HOSTNAME] = 'google.com' parsed_url[UrlParser.SCHEME] = 'ftp' parsed_url[UrlParser.USERNAME] = 'user' parsed_url[UrlParser.PASSWORD] = 'pwd' result = UrlParser.build_url(parsed_url) self.assertIsNotNone(result) self.assertEqual(correct_url, result)
def save(self, folder_path=None, configuration_type="running", vrf_management_name=None): """ Save device configuration to remote server :param folder_path: Full path to folder on remote server :param configuration_type: Type of configuration to save. Supports running and startup configuration :return Successful message or Exception """ if not configuration_type: configuration_type = "running-config" elif "-config" not in configuration_type: configuration_type = configuration_type.lower() + "-config" if configuration_type not in ["running-config", "startup-config"]: raise Exception(self.__class__.__name__, "Device doesn't support saving '{}' configuration type".format(configuration_type)) folder_path = self.get_path(folder_path) if not folder_path.endswith("/"): folder_path += "/" file_name = "{0}-{1}-{2}".format(self.resource_name, configuration_type, time.strftime("%d%m%y-%H%M%S", time.localtime())) connection_dict = UrlParser.parse_url(folder_path) if connection_dict.get(UrlParser.PATH).endswith("/"): file_path = connection_dict.get(UrlParser.PATH) + file_name else: file_path = connection_dict.get(UrlParser.PATH) + "/" + file_name save_command = "copy {config} {scheme} {host} {file_path}".format(config=configuration_type, scheme=connection_dict.get(UrlParser.SCHEME), host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) self.logger.info("Save configuration to file {0}".format(file_path)) output = self.cli_service.send_command(command=save_command, expected_str=self._default_prompt) if re.search(r"TFTP.*done", output): return "{0}".format(file_name) else: matched = re.match(r"TFTP:.*", output) if matched: error = matched.group() else: error = "Error during copy configuration" raise Exception(self.__class__.__name__, "Save configuration failed with error: {}".format(error))
def load_firmware(self, path, vrf_management_name=None): """Update firmware version on device by loading provided image, performs following steps: 1. Copy bin file from remote tftp server. 2. Clear in run config boot system section. 3. Set downloaded bin file as boot file and then reboot device. 4. Check if firmware was successfully installed. :param path: full path to firmware file on ftp/tftp location :param vrf_management_name: VRF Name :return: status / exception """ url = UrlParser.parse_url(path) if not url \ or UrlParser.FILENAME not in url \ or not url[UrlParser.FILENAME] \ or UrlParser.HOSTNAME not in url \ or not url[UrlParser.HOSTNAME] \ or UrlParser.SCHEME not in url \ or not url[UrlParser.SCHEME]: raise Exception('EricssonFirmwareOperations', 'Path is wrong or empty') image_version = '' image_version_match = re.search(r'(?=\d)\S+(?=.tar)', url[UrlParser.FILENAME], re.IGNORECASE) if image_version_match: image_version = image_version_match.group() expected_map = OrderedDict() expected_map['download\s+in\s+progress\s+[\[\(][Yy]/[Nn][\)\]]'] = ( lambda session: rerun_image_loading(session, 'release download {0}'.format(path))) expected_map['[\[\(][Yy]/[Nn][\)\]]'] = lambda session: session.send_line('y') expected_map['overwrite'] = lambda session: session.send_line('y') output = self.cli.send_command('release download {0}'.format(path), expected_map=expected_map) if not re.search('[Ii]nstallation [Cc]ompleted [Ss]uccessfully|Release distribution completed', output, re.IGNORECASE): message = '' match_error = re.search("can't connect.*connection timed out|Error.*\n|[Ll]ogin [Ff]ailed|\S+\s+fail(ed)?" + "|release download already in progress, unable to continue", output, re.IGNORECASE) if match_error: message = match_error.group() raise Exception('EricssonConfigurationOperations', 'Failed to load firmware: {0}. Please see logs for details'.format(message)) self.logger.info('Firmware has been successfully loaded to the device') if self.install_and_reboot(): current_version = self.cli.send_command('show version | include Version') if image_version not in current_version: raise Exception('EricssonFirmwareOperations', 'Failed to install provided image, please check logs') return 'Success'
def orchestration_save(self, mode="shallow", custom_params=None): """Orchestration Save command :param mode: :param custom_params: json with all required action to configure or remove vlans from certain port :return Serialized DriverResponseRoot to json :rtype json """ save_params = {'folder_path': '', 'configuration_type': 'running'} params = dict() if custom_params: params = jsonpickle.decode(custom_params) save_params.update(params.get('custom_params', {})) if save_params['folder_path'] and not save_params['folder_path'].endswith('/'): save_params['folder_path'] += '/' save_params['folder_path'] = self.get_path(save_params['folder_path']) url = UrlParser.parse_url(save_params['folder_path']) artifact_type = url[UrlParser.SCHEME].lower() self.logger.info('Start saving configuration') host = save_params['folder_path'].replace('{}:'.format(artifact_type), '') identifier = join(host, self.save(**save_params).strip(',')) saved_artifact = OrchestrationSavedArtifact(identifier=identifier, artifact_type=artifact_type) saved_artifact_info = OrchestrationSavedArtifactInfo(resource_name=self.resource_name, created_date=_get_snapshot_time_stamp(), restore_rules=self.get_restore_rules(), saved_artifact=saved_artifact) save_response = OrchestrationSaveResult(saved_artifacts_info=saved_artifact_info) self._validate_artifact_info(saved_artifact_info) return set_command_result(save_response)
def restore(self, path, configuration_type, restore_method='override', vrf_management_name=None): """ Restore configuration on device from remote server :param path: Full path to configuration file on remote server :param configuration_type: Type of configuration to restore. supports running and startup configuration :param restore_method: Type of restore method. Supports append and override. By default is override :param vrf_management_name: Not supported for Brocade Devices :return Successful message or Exception """ _is_need_reload = False if not restore_method: restore_method = "override" if not configuration_type: configuration_type = "running-config" elif "-config" not in configuration_type: configuration_type = configuration_type.lower() + "-config" if configuration_type not in ["running-config", "startup-config"]: raise Exception( self.__class__.__name__, "Device doesn't support restoring '{}' configuration type". format(configuration_type)) connection_dict = UrlParser.parse_url(path) if connection_dict.get(UrlParser.PATH).endswith("/"): file_path = connection_dict.get( UrlParser.PATH) + connection_dict.get(UrlParser.FILENAME) else: file_path = connection_dict.get( UrlParser.PATH) + "/" + connection_dict.get(UrlParser.FILENAME) if configuration_type == "startup-config" and restore_method.lower( ) == "append": raise Exception( self.__class__.__name__, "Device doesn't support restoring '{0}' configuration type with '{1}' method" .format(configuration_type, restore_method)) elif configuration_type == "running-config" and restore_method.lower( ) == "override": if self.session.session_type.lower() == 'console': restore_command = "copy {scheme} {config} {host} {file_path} overwrite"\ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) else: _is_need_reload = True configuration_type = "startup-config" restore_command = "copy {scheme} {config} {host} {file_path}" \ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) else: restore_command = "copy {scheme} {config} {host} {file_path}"\ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) output = self.cli_service.send_command( command=restore_command, expected_str=self._default_prompt) if re.search(r"TFTP.*done", output): self.logger.debug( "Reloading {} successfully".format(configuration_type)) if _is_need_reload: self.state_operations.reload() return 'Restore configuration completed.' else: matched = re.match(r"TFTP:.*", output) if matched: error = matched.group() else: error = "Error during copy configuration" raise Exception( self.__class__.__name__, "Restore configuration failed with error: {}".format(error))
def save(self, folder_path=None, configuration_type="running", vrf_management_name=None): """ Save device configuration to remote server :param folder_path: Full path to folder on remote server :param configuration_type: Type of configuration to save. Supports running and startup configuration :return Successful message or Exception """ if not configuration_type: configuration_type = "running-config" elif "-config" not in configuration_type: configuration_type = configuration_type.lower() + "-config" if configuration_type not in ["running-config", "startup-config"]: raise Exception( self.__class__.__name__, "Device doesn't support saving '{}' configuration type".format( configuration_type)) folder_path = self.get_path(folder_path) if not folder_path.endswith("/"): folder_path += "/" file_name = "{0}-{1}-{2}".format( self.resource_name, configuration_type, time.strftime("%d%m%y-%H%M%S", time.localtime())) connection_dict = UrlParser.parse_url(folder_path) if connection_dict.get(UrlParser.PATH).endswith("/"): file_path = connection_dict.get(UrlParser.PATH) + file_name else: file_path = connection_dict.get(UrlParser.PATH) + "/" + file_name save_command = "copy {config} {scheme} {host} {file_path}".format( config=configuration_type, scheme=connection_dict.get(UrlParser.SCHEME), host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) self.logger.info("Save configuration to file {0}".format(file_path)) output = self.cli_service.send_command( command=save_command, expected_str=self._default_prompt) if re.search(r"TFTP.*done", output): return "{0}".format(file_name) else: matched = re.match(r"TFTP:.*", output) if matched: error = matched.group() else: error = "Error during copy configuration" raise Exception( self.__class__.__name__, "Save configuration failed with error: {}".format(error))
def restore(self, path, configuration_type, restore_method='override', vrf_management_name=None): """ Restore configuration on device from remote server :param path: Full path to configuration file on remote server :param configuration_type: Type of configuration to restore. supports running and startup configuration :param restore_method: Type of restore method. Supports append and override. By default is override :param vrf_management_name: Not supported for Brocade Devices :return Successful message or Exception """ _is_need_reload = False if not restore_method: restore_method = "override" if not configuration_type: configuration_type = "running-config" elif "-config" not in configuration_type: configuration_type = configuration_type.lower() + "-config" if configuration_type not in ["running-config", "startup-config"]: raise Exception(self.__class__.__name__, "Device doesn't support restoring '{}' configuration type".format(configuration_type)) connection_dict = UrlParser.parse_url(path) if connection_dict.get(UrlParser.PATH).endswith("/"): file_path = connection_dict.get(UrlParser.PATH) + connection_dict.get(UrlParser.FILENAME) else: file_path = connection_dict.get(UrlParser.PATH) + "/" + connection_dict.get(UrlParser.FILENAME) if configuration_type == "startup-config" and restore_method.lower() == "append": raise Exception(self.__class__.__name__, "Device doesn't support restoring '{0}' configuration type with '{1}' method" .format(configuration_type, restore_method)) elif configuration_type == "running-config" and restore_method.lower() == "override": if self.session.session_type.lower() == 'console': restore_command = "copy {scheme} {config} {host} {file_path} overwrite"\ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) else: _is_need_reload = True configuration_type = "startup-config" restore_command = "copy {scheme} {config} {host} {file_path}" \ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) else: restore_command = "copy {scheme} {config} {host} {file_path}"\ .format(scheme=connection_dict.get(UrlParser.SCHEME), config=configuration_type, host=connection_dict.get(UrlParser.HOSTNAME), file_path=file_path) output = self.cli_service.send_command(command=restore_command, expected_str=self._default_prompt) if re.search(r"TFTP.*done", output): self.logger.debug("Reloading {} successfully".format(configuration_type)) if _is_need_reload: self.state_operations.reload() return 'Restore configuration completed.' else: matched = re.match(r"TFTP:.*", output) if matched: error = matched.group() else: error = "Error during copy configuration" raise Exception(self.__class__.__name__, "Restore configuration failed with error: {}".format(error))
def test_url_parser_host_only(self): url = '10.10.10.10' result = UrlParser.parse_url(url) self.assertIsNotNone(result)
def test_url_parser(self): url = 'ftp://*****:*****@google.com/folder1/file2' result = UrlParser.parse_url(url) self.assertIsNotNone(result)