def cmd_get(hname, fmt=None): """retrieve an smc element with its hierarchical name (hname) and display it. """ is_xml = (fmt == "xml") is_yaml = (fmt == "yaml") is_conf = (fmt == "conf") #todo error session_file_path = get_session_file_path() smc_client = SMCClient(session_file_path) try: smc_element = smc_client.get(hname) except ResolveError as err: raise CommandError(err) except (SMCOperationFailure) as err: raise CommandError(u"(SMC): " + unicode(err)) elt = smc_element.data if is_xml: xml = etree.tostring(elt, encoding='utf8', pretty_print=True) print_fmt(xml) elif is_conf: conf = etconfig.dumps(elt, print_root=True) print_fmt(conf) elif is_yaml: struct = etconfig.utils.el_to_struct(elt) print(yaml.dump(struct))
def cmd_show_hname(hname): """ convert a hierarchical name (hname) into the corresponding url """ session_file_path = get_session_file_path() smc_client = SMCClient(session_file_path) try: url = resolve_hname(smc_client.rest_client, hname) except ResolveError as err: raise CommandError(err) except (SMCOperationFailure) as err: raise CommandError(u"(SMC): " + unicode(err)) print_fmt(url)
def execute(self, target, operation=None, method="POST", data=None, params=None, print_only=False, files=None, **kwargs): """ target is either an hname or an instance of SMCElement """ hname = target.hname if isinstance(target, SMCElement) else target if operation: hname = hname + "/" + operation target_href = resolve_hname(self._client, hname) headers = { 'Accept': 'application/xml', } if not files: headers['Content-Type'] = 'application/json' if isinstance(target, SMCElement): headers['If-Match'] = target.etag xml = None if data is not None: resolve_elt_hnames(self._client, data, ignore_errors=print_only) xml = etree.tostring(data, encoding='utf8', pretty_print=True) headers['Content-Type'] = 'application/xml' method = method.upper() if print_only: print_fmt("{} {} {}\n{}\n{}", method, target_href, params, xml, files) return # exceptions propagated: eg SMCOperationFailure resp = self._client.request(method, target_href, headers=headers, params=params, data=xml, files=files) return resp.text
def cmd_list(hname, json=False, xml=False, links=False): """ list the sub-element under given hierarchical name (hname) """ session_file_path = get_session_file_path() smc_client = SMCClient(session_file_path) try: res = smc_client.list(hname) except ResolveError as err: raise CommandError(err) except (SMCOperationFailure) as err: raise CommandError(u"(SMC): " + unicode(err)) for name in sorted(res): print_fmt("{}", name)
def create(self, elt, hname=None, print_only=False): """send a POST request to the smc rest api to create an element :param etree.Element elt: represents the element data to create in xml e.g. <single_fw>...</single_fw> :param str hname: hierarchical name that will result in the url endoint. It can be omitted if the hname is the same as the element tag. e.g. hname="#single_fw" :returns: None :raises ResolveError: fails to convert a hname to a url (either the target or an url in an attribute of the element) :raises SMCOperationFailure: if creation unsuccessful """ if not hname: hname = elt.tag logger.debug("create resource hname=%s", hname) # ResolveError exception is propagated target_href = resolve_hname(self._client, hname) resolve_elt_hnames(self._client, elt, ignore_errors=print_only) xml = etree.tostring(elt, encoding='utf8', pretty_print=True) if print_only: print_fmt(CREATE_PRINT_FMT, verb="POST", target_href=target_href, tag=elt.tag, name=elt.get("name", "-"), xml=xml) return headers = { 'Content-Type': 'application/xml', 'Accept': 'application/xml' } resp = self._client.post(target_href, headers=headers, data=xml) logger.debug("status_code=%d", resp.status_code) logger.debug("text=%s", resp.text)
def delete_all_resources(smc_client, conf): """ iterate over all resources in the config file and try to delete them. Retries till there are failed resources and some resources were deleted in the previous iteration, to solve dependencies. """ resources = conf.xpath("resource") while resources: failed = _delete_all_resources(smc_client, resources) num_resources_remaining = len(failed) if num_resources_remaining == len(resources): print_fmt("{} resources could not be deleted", num_resources_remaining) for elt in failed: target_hname = _get_resource_name_to_delete(elt) print_fmt(" - {}", target_hname) return resources = failed
def update(self, smc_element, print_only=False): """ apply the change list of an smc element and send a rest call to update the object in the smc. :raises SMCOperationFailure: if update unsuccessful """ # todo refresh etags if object has changed # todo exc headers = { 'If-Match': smc_element.etag, 'Content-Type': 'application/xml' } target_href = resolve_hname(self._client, smc_element.hname) # todo error smc_element.apply_changes() resolve_elt_hnames(self._client, smc_element.data) xml = etree.tostring(smc_element.data, pretty_print=True) if print_only: elt = smc_element.data print_fmt(CREATE_PRINT_FMT, verb="PUT", target_href=target_href, tag=elt.tag, name=elt.get("name", "-"), xml=xml) return try: resp = self._client.put(target_href, headers=headers, data=xml) except SMCOperationFailure as exc: logger.error("Failed to update '%s'", unicode(exc)) raise exc smc_element.change_list = []
def _delete_all_resources(smc_client, resources): """ iterate over a list of resources and delete them :param list of Element resources: list of resource to delete :returns: list of Element resources that could not be deleted :rtype: list """ failed = [] for elt in resources: target_hname = _get_resource_name_to_delete(elt) if not target_hname: failed.append(elt) continue # print_values(target_hname=target_hname) try: do_delete_elt(smc_client, target_hname, elt) print_fmt("- delete {} successful", target_hname) except (ResolveError, SMCOperationFailure) as exc: failed.append(elt) return failed
def run_script(smc_client, filename, print_only=False, preprocess_only=False, user_variables=None, variable_files=None, ignore_errors=False, delete_mode=False, cleanup_mode=False): """execute a script :param SMCClient smc_client: client to send requests to smc :param str filename: name of the file containing the script :param bool print_only: does not execute the script. only print :param bool preprocess_only: show the script after the template preprocessing :param dict user_variables: dict of name/value used as variables for template preprocessing :returns: None :raises IOError: if filename could not be read :raises TemplateLookupException: failed to include a file :raises NameError: if a variable cannot be resolved :raises SyntaxException: syntax error during preprocessing :raises ElementConfError: failed to parse the filename (after processing) :raises InvalidSessionError: not logged in or session expired :raises SMCOperationFailure: is smc server responds with status_code error """ api_version = smc_client.api_version variables = {'API_VERSION': api_version, 'SCRIPT': smc_client} lookup_dir = os.path.dirname(os.path.abspath(filename)) var_config_file_name = lookup_dir + "/variables.cnf" logger.debug("var_config_file_name=%s", var_config_file_name) # default values from variables.cnf if os.path.isfile(var_config_file_name): logger.debug("opening =%s", var_config_file_name) var_elts = load_config_file(var_config_file_name) logger.debug("var_elts=%s", var_elts) for var_elt in var_elts: name = var_elt.get("name") value = var_elt.get("default") variables[name] = value logger.debug("VAR: %s=%s", name, value) # variables obtained from conf implicit file files = glob('*.cnfvars') for f in files: file_variables = read_variables_from_conf(f) variables.update(file_variables) # variables obtained from conf explicit file (--var-file) for f in variable_files: if not os.path.isfile(f): raise RunScriptError( "variable config file '{}' not found ".format(f)) file_variables = read_variables_from_conf(f) variables.update(file_variables) # variables obtained from env.variables (take precedence from file) for name, value in os.environ.items(): prefix = 'CNF_VAR_' if name.startswith(prefix): var_name = name[len(prefix):] variables[var_name] = value # variables obtained from command-line (take precedence from both # file and env.var) if user_variables: variables.update(user_variables) if preprocess_only: rendered = preprocess_config_file(filename, variables) print(rendered) return conf = load_config_file(filename, variables) if delete_mode or cleanup_mode: print("Cleaning up...") delete_all_resources(smc_client, conf) if delete_mode: return operations = dict(login=do_login, logout=do_logout, resource=do_create_elt, delete=do_delete_elt, update=do_update_elt, command=do_execute, get=do_execute) print("Applying config...") for elt in conf: operation = elt.tag target_hname = _get_target_hname(elt) if operation == 'resource': name = elt[0].attrib.get("name", "") op_text = "{} {}/{}...".format(operation, target_hname, name) else: op_text = "{} {}...".format(operation, target_hname) logging.info("script command: %s", op_text) print_fmt("- {}", op_text) if operation not in operations: raise RunScriptError("invalid operation: " + operation) fun = operations.get(operation) try: fun(smc_client, target_hname, elt, print_only) except (SMCOperationFailure, ResolveError) as exc: if ignore_errors: print_err(" => Ignoring error: {}", unicode(exc)) continue raise SMCOperationFailure(unicode(exc)) return