def runner(cls, api, parsed_args): target_info = 'vManage URL: "{url}"'.format(url=api.base_url) if parsed_args.workdir is None \ else 'Local workdir: "{workdir}"'.format(workdir=parsed_args.workdir) cls.log_info('Starting list: %s', target_info) backend = parsed_args.workdir if parsed_args.workdir is not None else api matched_item_iter = ((item_name, item_id, tag, info) for tag, info, index, item_cls in cls.index_iter( backend, catalog_entries(*parsed_args.tags)) for item_id, item_name in index if parsed_args.regex is None or regex_search( parsed_args.regex, item_name, item_id)) results = Table('Name', 'ID', 'Tag', 'Type') results.extend(matched_item_iter) cls.log_info('List criteria matched %s items', len(results)) if len(results) > 0: print_buffer = [] print_buffer.extend(results.pretty_iter()) if parsed_args.csv is not None: results.save(parsed_args.csv) cls.log_info('Table exported as %s', parsed_args.csv) else: print('\n'.join(print_buffer))
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(cls, api, parsed_args): cls.log_info('Starting delete%s: vManage URL: "%s"', ', DRY-RUN mode' if parsed_args.dryrun else '', api.base_url) if parsed_args.detach: try: template_index = DeviceTemplateIndex.get_raise(api) # Detach WAN Edge templates action_list = cls.detach_template(api, template_index, DeviceTemplateIndex.is_not_vsmart) if len(action_list) == 0: cls.log_info('No WAN Edge attached') else: cls.wait_actions(api, action_list, 'detaching WAN Edge templates') # Deactivate vSmart policy action_list = cls.deactivate_policy(api) if len(action_list) == 0: cls.log_info('No vSmart policy activated') else: cls.wait_actions(api, action_list, 'deactivating vSmart policy') # Detach vSmart template action_list = cls.detach_template(api, template_index, DeviceTemplateIndex.is_vsmart) if len(action_list) == 0: cls.log_info('No vSmart attached') else: cls.wait_actions(api, action_list, 'detaching vSmart template') except RestAPIException as ex: cls.log_critical('Detach failed: %s', ex) for tag in ordered_tags(parsed_args.tag, parsed_args.tag != CATALOG_TAG_ALL): cls.log_info('Inspecting %s items', tag) matched_item_iter = ( (item_name, item_id, item_cls, info) for _, info, index, item_cls in cls.index_iter(api, catalog_entries(tag)) for item_id, item_name in index if parsed_args.regex is None or regex_search(parsed_args.regex, item_name) ) for item_name, item_id, item_cls, info in matched_item_iter: item = item_cls.get(api, item_id) if item is None: cls.log_warning('Failed retrieving %s %s', info, item_name) continue if item.is_readonly or item.is_system: cls.log_debug('Skipped %s %s %s', 'read-only' if item.is_readonly else 'system', info, item_name) continue if parsed_args.dryrun: cls.log_info('DRY-RUN: Delete %s %s', info, item_name) continue if api.delete(item_cls.api_path.delete, item_id): cls.log_info('Done: Delete %s %s', info, item_name) else: cls.log_warning('Failed deleting %s %s', info, item_name)
def config_table(cls, api, parsed_args): target_info = 'vManage URL: "{url}"'.format(url=api.base_url) if parsed_args.workdir is None \ else 'Local workdir: "{workdir}"'.format(workdir=parsed_args.workdir) cls.log_info('Starting list configuration: %s', target_info) backend = parsed_args.workdir if parsed_args.workdir is not None else api matched_item_iter = ( (item_name, item_id, tag, info) for tag, info, index, item_cls in cls.index_iter(backend, catalog_entries(*parsed_args.tags)) for item_id, item_name in index if parsed_args.regex is None or regex_search(parsed_args.regex, item_name, item_id) ) results = Table('Name', 'ID', 'Tag', 'Type') results.extend(matched_item_iter) return results
def device_template_values(cls, api, show_args): def item_matches(item_name, item_id): if show_args.id is not None: return item_id == show_args.id if show_args.name is not None: return item_name == show_args.name return regex_search(show_args.regex, item_name) def template_values(ext_name, template_name, template_id): if show_args.workdir is None: # Load from vManage via API devices_attached = DeviceTemplateAttached.get(api, template_id) if devices_attached is None: cls.log_error('Failed to retrieve %s attached devices', template_name) return None try: uuid_list = [uuid for uuid, _ in devices_attached] values = DeviceTemplateValues( api.post( DeviceTemplateValues.api_params( template_id, uuid_list), DeviceTemplateValues.api_path.post)) except RestAPIException: cls.log_error('Failed to retrieve %s values', template_name) return None else: # Load from local backup values = DeviceTemplateValues.load(show_args.workdir, ext_name, template_name, template_id) if values is None: cls.log_debug('Skipped %s. No template values file found.', template_name) return values print_buffer = [] backend = show_args.workdir if show_args.workdir is not None else api matched_item_iter = ((index.need_extended_name, item_name, item_id, tag, info) for tag, info, index, item_cls in cls.index_iter( backend, catalog_entries('template_device')) for item_id, item_name in index if item_matches(item_name, item_id) and issubclass(item_cls, DeviceTemplate)) for use_ext_name, item_name, item_id, tag, info in matched_item_iter: attached_values = template_values(use_ext_name, item_name, item_id) if attached_values is None: continue cls.log_info('Inspecting %s %s values', info, item_name) var_names = attached_values.title_dict() for csv_id, csv_name, entry in attached_values: print_grp = [ 'Template {name}, device {device}:'.format(name=item_name, device=csv_name or csv_id) ] results = Table('Name', 'Value', 'Variable') results.extend((var_names.get(var, '<not found>'), value, var) for var, value in entry.items()) if len(results) > 0: if show_args.csv is not None: filename = 'template_values_{name}_{id}.csv'.format( name=filename_safe(item_name, lower=True), id=csv_name or csv_id) results.save(Path(show_args.csv, filename)) print_grp.extend(results.pretty_iter()) print_buffer.append('\n'.join(print_grp)) if len(print_buffer) > 0: if show_args.csv is not None: cls.log_info('Files saved under directory %s', show_args.csv) else: print('\n\n'.join(print_buffer)) else: match_type = 'ID' if show_args.id is not None else 'name' if show_args.name is not None else 'regex' cls.log_warning('No items found with the %s provided', match_type)
def runner(cls, api, parsed_args): def load_items(index, item_cls): item_iter = ((item_id, item_cls.load(parsed_args.workdir, index.need_extended_name, item_name, item_id)) for item_id, item_name in index) return ((item_id, item_obj) for item_id, item_obj in item_iter if item_obj is not None) cls.log_info( 'Starting restore%s: Local workdir: "%s" > vManage URL: "%s"', ', DRY-RUN mode' if parsed_args.dryrun else '', parsed_args.workdir, api.base_url) local_info = ServerInfo.load(parsed_args.workdir) # Server info file may not be present (e.g. backup from older Sastre releases) if local_info is not None and is_version_newer( api.server_version, local_info.server_version): cls.log_warning( 'Target vManage release (%s) is older than the release used in backup (%s). ' + 'Items may fail to be restored due to incompatibilities across releases.', api.server_version, local_info.server_version) cls.log_info('Loading existing items from target vManage') target_all_items_map = { hash(type(index)): {item_name: item_id for item_id, item_name in index} for _, _, index, item_cls in cls.index_iter( api, catalog_entries(CATALOG_TAG_ALL)) } cls.log_info('Identifying items to be pushed') id_mapping = { } # {<old_id>: <new_id>}, used to replace old (saved) item ids with new (target) ids restore_list = [ ] # [ (<info>, <index_cls>, [(<item_id>, <item>, <id_on_target>), ...]), ...] dependency_set = set() # {<item_id>, ...} match_set = set() # {<item_id>, ...} for tag in ordered_tags(parsed_args.tag): cls.log_info('Inspecting %s items', tag) tag_iter = ((info, index, load_items(index, item_cls)) for _, info, index, item_cls in cls.index_iter( parsed_args.workdir, catalog_entries(tag))) for info, index, loaded_items_iter in tag_iter: target_item_map = target_all_items_map.get(hash(type(index))) if target_item_map is None: # Logging at warning level because the backup files did have this item cls.log_warning( 'Will skip %s, item not supported by target vManage', info) continue restore_item_list = [] for item_id, item in loaded_items_iter: target_id = target_item_map.get(item.name) if target_id is not None: # Item already exists on target vManage, record item id from target if item_id != target_id: id_mapping[item_id] = target_id if not parsed_args.force: # Existing item on target vManage will be used, i.e. will not overwrite it cls.log_debug( 'Will skip %s %s, item already on target vManage', info, item.name) continue if item.is_readonly: cls.log_debug('Will skip read-only %s %s', info, item.name) continue item_matches = ((parsed_args.tag == CATALOG_TAG_ALL or parsed_args.tag == tag) and (parsed_args.regex is None or regex_search( parsed_args.regex, item.name))) if item_matches: match_set.add(item_id) if item_matches or item_id in dependency_set: # A target_id that is not None signals a push operation, as opposed to post. # target_id will be None unless --force is specified and item name is on target restore_item_list.append((item_id, item, target_id)) dependency_set.update(item.id_references_set) if len(restore_item_list) > 0: restore_list.append((info, index, restore_item_list)) log_prefix = 'DRY-RUN: ' if parsed_args.dryrun else '' if len(restore_list) > 0: cls.log_info('%sPushing items to vManage', log_prefix) # Items were added to restore_list following ordered_tags() order (i.e. higher level items before lower # level items). The reverse order needs to be followed on restore. for info, index, restore_item_list in reversed(restore_list): pushed_item_dict = {} for item_id, item, target_id in restore_item_list: op_info = 'Create' if target_id is None else 'Update' reason = ' (dependency)' if item_id in dependency_set - match_set else '' try: if target_id is None: # Create new item if parsed_args.dryrun: cls.log_info('%s%s %s %s%s', log_prefix, op_info, info, item.name, reason) continue # Not using item id returned from post because post can return empty (e.g. local policies) api.post(item.post_data(id_mapping), item.api_path.post) pushed_item_dict[item.name] = item_id else: # Update existing item update_data = item.put_data(id_mapping) if item.get_raise(api, target_id).is_equal(update_data): cls.log_debug('%s%s skipped (no diffs) %s %s', log_prefix, op_info, info, item.name) continue if parsed_args.dryrun: cls.log_info('%s%s %s %s%s', log_prefix, op_info, info, item.name, reason) continue put_eval = UpdateEval( api.put(update_data, item.api_path.put, target_id)) if put_eval.need_reattach: if put_eval.is_master: cls.log_info( 'Updating %s %s requires reattach', info, item.name) action_list = cls.attach_template( api, parsed_args.workdir, index.need_extended_name, [(item.name, item_id, target_id)]) else: cls.log_info( 'Updating %s %s requires reattach of affected templates', info, item.name) target_templates = { item_id: item_name for item_id, item_name in DeviceTemplateIndex.get_raise(api) } templates_iter = ( (target_templates[tgt_id], tgt_id) for tgt_id in put_eval.templates_affected_iter()) action_list = cls.reattach_template( api, templates_iter) cls.wait_actions(api, action_list, 'reattaching templates', raise_on_failure=True) elif put_eval.need_reactivate: cls.log_info( 'Updating %s %s requires vSmart policy reactivate', info, item.name) action_list = cls.activate_policy( api, *PolicyVsmartIndex.get_raise( api).active_policy, is_edited=True) cls.wait_actions(api, action_list, 'reactivating vSmart policy', raise_on_failure=True) except (RestAPIException, WaitActionsException) as ex: cls.log_error('Failed %s %s %s%s: %s', op_info, info, item.name, reason, ex) else: cls.log_info('Done: %s %s %s%s', op_info, info, item.name, reason) # Read new ids from target and update id_mapping try: new_target_item_map = { item_name: item_id for item_id, item_name in index.get_raise(api) } for item_name, old_item_id in pushed_item_dict.items(): id_mapping[old_item_id] = new_target_item_map[ item_name] except RestAPIException as ex: cls.log_critical('Failed retrieving %s: %s', info, ex) break else: cls.log_info('%sNo items to push', log_prefix) if parsed_args.attach: try: target_templates = { item_name: item_id for item_id, item_name in DeviceTemplateIndex.get_raise( api) } target_policies = { item_name: item_id for item_id, item_name in PolicyVsmartIndex.get_raise(api) } saved_template_index = DeviceTemplateIndex.load( parsed_args.workdir, raise_not_found=True) attach_common_args = (api, parsed_args.workdir, saved_template_index.need_extended_name) # Attach WAN Edge templates edge_templates_iter = ( (saved_name, saved_id, target_templates.get(saved_name)) for saved_id, saved_name in saved_template_index. filtered_iter(DeviceTemplateIndex.is_not_vsmart)) wan_edge_set = { uuid for uuid, _ in EdgeInventory.get_raise(api) } action_list = cls.attach_template(*attach_common_args, edge_templates_iter, wan_edge_set) if len(action_list) == 0: cls.log_info('No WAN Edge attachments needed') else: cls.wait_actions(api, action_list, 'attaching WAN Edge templates') # Attach vSmart template vsmart_templates_iter = ( (saved_name, saved_id, target_templates.get(saved_name)) for saved_id, saved_name in saved_template_index. filtered_iter(DeviceTemplateIndex.is_vsmart)) vsmart_set = { uuid for uuid, _ in ControlInventory.get_raise( api).filtered_iter(ControlInventory.is_vsmart) } action_list = cls.attach_template(*attach_common_args, vsmart_templates_iter, vsmart_set) if len(action_list) == 0: cls.log_info('No vSmart attachments needed') else: cls.wait_actions(api, action_list, 'attaching vSmart template') # Activate vSmart policy _, policy_name = PolicyVsmartIndex.load( parsed_args.workdir, raise_not_found=True).active_policy action_list = cls.activate_policy( api, target_policies.get(policy_name), policy_name) if len(action_list) == 0: cls.log_info('No vSmart policy to activate') else: cls.wait_actions(api, action_list, 'activating vSmart policy') except (RestAPIException, FileNotFoundError) as ex: cls.log_critical('Attach failed: %s', ex)