def runner(cls, api, parsed_args): cls.log_info('Starting backup: vManage URL: "%s" > Local workdir: "%s"', api.base_url, parsed_args.workdir) # Backup workdir must be empty for a new backup saved_workdir = cls.clean_workdir(parsed_args.workdir) if saved_workdir: cls.log_info('Previous backup under "%s" was saved as "%s"', parsed_args.workdir, saved_workdir) target_info = ServerInfo(server_version=api.server_version) if target_info.save(parsed_args.workdir): cls.log_info('Saved vManage server information') if CATALOG_TAG_ALL in parsed_args.tags: # Items without index files to be included with tag 'all' edge_certs = EdgeCertificate.get(api) if edge_certs is None: cls.log_error('Failed backup WAN edge certificates') elif edge_certs.save(parsed_args.workdir): cls.log_info('Saved WAN edge certificates') for _, info, index_cls, item_cls in catalog_entries(*parsed_args.tags): item_index = index_cls.get(api) if item_index is None: cls.log_debug('Skipped %s, item not supported by this vManage', info) continue if item_index.save(parsed_args.workdir): cls.log_info('Saved %s index', info) matched_item_iter = ( (item_id, item_name) for item_id, item_name in item_index if parsed_args.regex is None or regex_search(parsed_args.regex, item_name) ) for item_id, item_name in matched_item_iter: item = item_cls.get(api, item_id) if item is None: cls.log_error('Failed backup %s %s', info, item_name) continue if item.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): cls.log_info('Done %s %s', info, item_name) # Special case for DeviceTemplateAttached and DeviceTemplateValues if isinstance(item, DeviceTemplate): devices_attached = DeviceTemplateAttached.get(api, item_id) if devices_attached is None: cls.log_error('Failed backup %s %s attached devices', info, item_name) continue if devices_attached.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): cls.log_info('Done %s %s attached devices', info, item_name) else: cls.log_debug('Skipped %s %s attached devices, none found', info, item_name) continue try: uuid_list = [uuid for uuid, _ in devices_attached] values = DeviceTemplateValues(api.post(DeviceTemplateValues.api_params(item_id, uuid_list), DeviceTemplateValues.api_path.post)) if values.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): cls.log_info('Done %s %s values', info, item_name) except RestAPIException as ex: cls.log_error('Failed backup %s %s values: %s', info, item_name, ex)
def runner(self, parsed_args, api, task_output=None): self.log_info('Starting backup: vManage URL: "%s" -> Local workdir: "%s"', api.base_url, parsed_args.workdir) # Backup workdir must be empty for a new backup saved_workdir = clean_dir(parsed_args.workdir, max_saved=0 if parsed_args.no_rollover else 99) if saved_workdir: self.log_info('Previous backup under "%s" was saved as "%s"', parsed_args.workdir, saved_workdir) target_info = ServerInfo(server_version=api.server_version) if target_info.save(parsed_args.workdir): self.log_info('Saved vManage server information') # Backup items not registered to the catalog, but to be included when tag is 'all' if CATALOG_TAG_ALL in parsed_args.tags: edge_certs = EdgeCertificate.get(api) if edge_certs is None: self.log_error('Failed backup WAN edge certificates') elif edge_certs.save(parsed_args.workdir): self.log_info('Saved WAN edge certificates') for inventory, info in ((EdgeInventory.get(api), 'WAN edge'), (ControlInventory.get(api), 'controller')): if inventory is None: self.log_error('Failed retrieving %s inventory', info) continue for uuid, _, hostname, _ in inventory.extended_iter(): if hostname is None: self.log_debug('Skipping %s, no hostname', uuid) continue for item, config_type in ((DeviceConfig.get(api, DeviceConfig.api_params(uuid)), 'CFS'), (DeviceConfigRFS.get(api, DeviceConfigRFS.api_params(uuid)), 'RFS')): if item is None: self.log_error('Failed backup %s device configuration %s', config_type, hostname) continue if item.save(parsed_args.workdir, item_name=hostname, item_id=uuid): self.log_info('Done %s device configuration %s', config_type, hostname) # Backup items registered to the catalog for _, info, index_cls, item_cls in catalog_iter(*parsed_args.tags, version=api.server_version): item_index = index_cls.get(api) if item_index is None: self.log_debug('Skipped %s, item not supported by this vManage', info) continue if item_index.save(parsed_args.workdir): self.log_info('Saved %s index', info) matched_item_iter = ( (item_id, item_name) for item_id, item_name in item_index if parsed_args.regex is None or regex_search(parsed_args.regex, item_name) ) for item_id, item_name in matched_item_iter: item = item_cls.get(api, item_id) if item is None: self.log_error('Failed backup %s %s', info, item_name) continue if item.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): self.log_info('Done %s %s', info, item_name) # Special case for DeviceTemplateAttached and DeviceTemplateValues if isinstance(item, DeviceTemplate): devices_attached = DeviceTemplateAttached.get(api, item_id) if devices_attached is None: self.log_error('Failed backup %s %s attached devices', info, item_name) continue if devices_attached.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): self.log_info('Done %s %s attached devices', info, item_name) else: self.log_debug('Skipped %s %s attached devices, none found', info, item_name) continue try: uuid_list = [uuid for uuid, _ in devices_attached] values = DeviceTemplateValues(api.post(DeviceTemplateValues.api_params(item_id, uuid_list), DeviceTemplateValues.api_path.post)) if values.save(parsed_args.workdir, item_index.need_extended_name, item_name, item_id): self.log_info('Done %s %s values', info, item_name) except RestAPIException as ex: self.log_error('Failed backup %s %s values: %s', info, item_name, ex)
def runner(self, parsed_args, api=None, task_output=None): source_info = f'Local workdir: "{parsed_args.workdir}"' if api is None else f'vManage URL: "{api.base_url}"' self.log_info('Starting migrate: %s %s -> %s Local output dir: "%s"', source_info, parsed_args.from_version, parsed_args.to_version, parsed_args.output) # Output directory must be empty for a new migration saved_output = clean_dir(parsed_args.output, max_saved=0 if parsed_args.no_rollover else 99) if saved_output: self.log_info('Previous migration under "%s" was saved as "%s"', parsed_args.output, saved_output) if api is None: backend = parsed_args.workdir local_info = ServerInfo.load(backend) server_version = local_info.server_version if local_info is not None else None else: backend = api server_version = backend.server_version try: # Load migration processors loaded_processors = { FeatureTemplate: FeatureProcessor.load(from_version=parsed_args.from_version, to_version=parsed_args.to_version), DeviceTemplate: DeviceProcessor.load(from_version=parsed_args.from_version, to_version=parsed_args.to_version) } self.log_info('Loaded template migration recipes') server_info = ServerInfo(server_version=parsed_args.to_version) if server_info.save(parsed_args.output): self.log_info('Saved vManage server information') id_mapping = {} # {<old_id>: <new_id>} for tag in ordered_tags(CATALOG_TAG_ALL, reverse=True): self.log_info('Inspecting %s items', tag) for _, info, index_cls, item_cls in catalog_iter(tag, version=server_version): item_index = self.index_get(index_cls, backend) if item_index is None: self.log_debug('Skipped %s, none found', info) continue name_set = {item_name for item_id, item_name in item_index} is_bad_name = False export_list = [] id_hint_map = {item_name: item_id for item_id, item_name in item_index} for item_id, item_name in item_index: item = self.item_get(item_cls, backend, item_id, item_name, item_index.need_extended_name) if item is None: self.log_error('Failed loading %s %s', info, item_name) continue try: item_processor = loaded_processors.get(item_cls) if item_processor is None: raise StopProcessorException() self.log_debug('Evaluating %s %s', info, item_name) if not item_processor.is_in_scope(item, migrate_all=(parsed_args.scope == 'all')): self.log_debug('Skipping %s, migration not necessary', item_name) raise StopProcessorException() new_name, is_valid = item.get_new_name(parsed_args.name) if not is_valid: self.log_error('New %s name is not valid: %s', info, new_name) is_bad_name = True raise StopProcessorException() if new_name in name_set: self.log_error('New %s name collision: %s -> %s', info, item_name, new_name) is_bad_name = True raise StopProcessorException() name_set.add(new_name) new_id = str(uuid4()) new_payload, trace_log = item_processor.eval(item, new_name, new_id) for trace in trace_log: self.log_debug('Processor: %s', trace) if item.is_equal(new_payload): self.log_debug('Skipping %s, no changes', item_name) raise StopProcessorException() new_item = item_cls(update_ids(id_mapping, new_payload)) id_mapping[item_id] = new_id id_hint_map[new_name] = new_id if item_processor.replace_original(): self.log_debug('Migrated replaces original: %s -> %s', item_name, new_name) item = new_item else: self.log_debug('Migrated adds to original: %s + %s', item_name, new_name) export_list.append(new_item) except StopProcessorException: pass export_list.append(item) if is_bad_name: raise TaskException(f'One or more new {info} names are not valid') if not export_list: self.log_info('No %s migrated', info) continue if issubclass(item_cls, FeatureTemplate): for factory_default in (factory_cedge_aaa, factory_cedge_global): if any(factory_default.name == elem.name for elem in export_list): self.log_debug('Using existing factory %s %s', info, factory_default.name) # Updating because device processor always use the built-in IDs id_mapping[factory_default.uuid] = id_hint_map[factory_default.name] else: export_list.append(factory_default) id_hint_map[factory_default.name] = factory_default.uuid self.log_debug('Added factory %s %s', info, factory_default.name) new_item_index = index_cls.create(export_list, id_hint_map) if new_item_index.save(parsed_args.output): self.log_info('Saved %s index', info) for new_item in export_list: if new_item.save(parsed_args.output, new_item_index.need_extended_name, new_item.name, id_hint_map[new_item.name]): self.log_info('Saved %s %s', info, new_item.name) except (ProcessorException, TaskException) as ex: self.log_critical('Migration aborted: %s', ex)