PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/jboss7_cli.py |
""" Module for low-level interaction with JbossAS7 through CLI. This module exposes two ways of interaction with the CLI, either through commands or operations. .. note:: Following JBoss documentation (https://developer.jboss.org/wiki/CommandLineInterface): "Operations are considered a low level but comprehensive way to manage the AS controller, i.e. if it can't be done with operations it can't be done in any other way. Commands, on the other hand, are more user-friendly in syntax, although most of them still translate into operation requests and some of them even into a few composite operation requests, i.e. commands also simplify some management operations from the user's point of view." The difference between calling a command or operation is in handling the result. Commands return a zero return code if operation is successful or return non-zero return code and print an error to standard output in plain text, in case of an error. Operations return a json-like structure, that contain more information about the result. In case of a failure, they also return a specific return code. This module parses the output from the operations and returns it as a dictionary so that an execution of an operation can then be verified against specific errors. In order to run each function, jboss_config dictionary with the following properties must be passed: * cli_path: the path to jboss-cli script, for example: '/opt/jboss/jboss-7.0/bin/jboss-cli.sh' * controller: the IP address and port of controller, for example: 10.11.12.13:9999 * cli_user: username to connect to jboss administration console if necessary * cli_password: password to connect to jboss administration console if necessary Example: .. code-block:: yaml jboss_config: cli_path: '/opt/jboss/jboss-7.0/bin/jboss-cli.sh' controller: 10.11.12.13:9999 cli_user: 'jbossadm' cli_password: 'jbossadm' """ import logging import pprint import re import time from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) def run_command(jboss_config, command, fail_on_error=True): """ Execute a command against jboss instance through the CLI interface. jboss_config Configuration dictionary with properties specified above. command Command to execute against jboss instance fail_on_error (default=True) Is true, raise CommandExecutionError exception if execution fails. If false, 'success' property of the returned dictionary is set to False CLI Example: .. code-block:: bash salt '*' jboss7_cli.run_command '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_command """ cli_command_result = _call_cli(jboss_config, command) if cli_command_result["retcode"] == 0: cli_command_result["success"] = True else: if fail_on_error: raise CommandExecutionError( """Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( **cli_command_result ) ) else: cli_command_result["success"] = False return cli_command_result def run_operation(jboss_config, operation, fail_on_error=True, retries=1): """ Execute an operation against jboss instance through the CLI interface. jboss_config Configuration dictionary with properties specified above. operation An operation to execute against jboss instance fail_on_error (default=True) Is true, raise CommandExecutionError exception if execution fails. If false, 'success' property of the returned dictionary is set to False retries: Number of retries in case of "JBAS012144: Could not connect to remote" error. CLI Example: .. code-block:: bash salt '*' jboss7_cli.run_operation '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_operation """ cli_command_result = _call_cli(jboss_config, operation, retries) if cli_command_result["retcode"] == 0: if _is_cli_output(cli_command_result["stdout"]): cli_result = _parse(cli_command_result["stdout"]) cli_result["success"] = cli_result["outcome"] == "success" else: raise CommandExecutionError( "Operation has returned unparseable output: {}".format( cli_command_result["stdout"] ) ) else: if _is_cli_output(cli_command_result["stdout"]): cli_result = _parse(cli_command_result["stdout"]) cli_result["success"] = False match = re.search(r"^(JBAS\d+):", cli_result["failure-description"]) # if match is None then check for wildfly error code if match is None: match = re.search(r"^(WFLYCTL\d+):", cli_result["failure-description"]) if match is not None: cli_result["err_code"] = match.group(1) else: # Could not find err_code log.error("Jboss 7 operation failed! Error Code could not be found!") cli_result["err_code"] = "-1" cli_result["stdout"] = cli_command_result["stdout"] else: if fail_on_error: raise CommandExecutionError( """Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( **cli_command_result ) ) else: cli_result = { "success": False, "stdout": cli_command_result["stdout"], "stderr": cli_command_result["stderr"], "retcode": cli_command_result["retcode"], } return cli_result def _call_cli(jboss_config, command, retries=1): command_segments = [ jboss_config["cli_path"], "--connect", '--controller="{}"'.format(jboss_config["controller"]), ] if "cli_user" in jboss_config.keys(): command_segments.append('--user="{}"'.format(jboss_config["cli_user"])) if "cli_password" in jboss_config.keys(): command_segments.append('--password="{}"'.format(jboss_config["cli_password"])) command_segments.append(f'--command="{__escape_command(command)}"') cli_script = " ".join(command_segments) cli_command_result = __salt__["cmd.run_all"](cli_script) log.debug("cli_command_result=%s", cli_command_result) log.debug("========= STDOUT:\n%s", cli_command_result["stdout"]) log.debug("========= STDERR:\n%s", cli_command_result["stderr"]) log.debug("========= RETCODE: %d", cli_command_result["retcode"]) if cli_command_result["retcode"] == 127: raise CommandExecutionError( "Could not execute jboss-cli.sh script. Have you specified server_dir" " variable correctly?\nCurrent CLI path: {cli_path}. ".format( cli_path=jboss_config["cli_path"] ) ) if ( cli_command_result["retcode"] == 1 and "Unable to authenticate against controller" in cli_command_result["stderr"] ): raise CommandExecutionError( "Could not authenticate against controller, please check username and" " password for the management console. Err code: {retcode}, stdout:" " {stdout}, stderr: {stderr}".format(**cli_command_result) ) # TODO add WFLYCTL code if ( cli_command_result["retcode"] == 1 and "JBAS012144" in cli_command_result["stderr"] and retries > 0 ): # Cannot connect to cli log.debug("Command failed, retrying... (%d tries left)", retries) time.sleep(3) return _call_cli(jboss_config, command, retries - 1) return cli_command_result def __escape_command(command): """ This function escapes the command so that can be passed in the command line to JBoss CLI. Escaping commands passed to jboss is extremely confusing. If you want to save a binding that contains a single backslash character read the following explanation. A sample value, let's say "a\b" (with single backslash), that is saved in the config.xml file: <bindings> <simple name="java:/app/binding1" value="a\b"/> </bindings> Eventhough it is just a single "\" if you want to read it from command line you will get: /opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller=ip_addr:9999 --user=user --password=pass --command="/subsystem=naming/binding=\"java:/app/binding1\":read-resource" { "outcome" => "success", "result" => { "binding-type" => "simple", "value" => "a\\b" } } So, now you have two backslashes in the output, even though in the configuration file you have one. Now, if you want to update this property, the easiest thing to do is to create a file with appropriate command: /tmp/update-binding.cli: ---- /subsystem=naming/binding="java:/app/binding1":write-attribute(name=value, value="a\\\\b") ---- And run cli command: ${JBOSS_HOME}/bin/jboss-cli.sh --connect --controller=ip_addr:9999 --user=user --password=pass --file="/tmp/update-binding.cli" As you can see, here you need 4 backslashes to save it as one to the configuration file. Run it and go to the configuration file to check. (You may need to reload jboss afterwards: ${JBOSS_HOME}/bin/jboss-cli.sh --connect --controller=ip_addr:9999 --user=user --password=pass --command=":reload" ) But if you want to run the same update operation directly from command line, prepare yourself for more escaping: ${JBOSS_HOME}/bin/jboss-cli.sh --connect --controller=ip_addr:9999 --user=user --password=pass --command="/subsystem=naming/binding=\"java:/app/binding1\":write-attribute(name=value, value=\"a\\\\\\\\b\")" So, here you need 8 backslashes to force JBoss to save it as one. To sum up this behavior: (1) 1 backslash in configuration file (2) 2 backslashes when reading (3) 4 backslashes when writing from file (4) 8 backslashes when writing from command line ... are all the same thing:) Remember that the command that comes in is already (3) format. Now we need to escape it further to be able to pass it to command line. """ result = command.replace("\\", "\\\\") # replace \ -> \\ result = result.replace('"', '\\"') # replace " -> \" return result def _is_cli_output(text): cli_re = re.compile(r"^\s*{.+}\s*$", re.DOTALL) if cli_re.search(text): return True else: return False def _parse(cli_output): tokens = __tokenize(cli_output) result = __process_tokens(tokens) log.debug("=== RESULT: %s", pprint.pformat(result)) return result def __process_tokens(tokens): result, token_no = __process_tokens_internal(tokens) return result def __process_tokens_internal(tokens, start_at=0): if __is_dict_start(tokens[start_at]) and start_at == 0: # the top object return __process_tokens_internal(tokens, start_at=1) log.debug("__process_tokens, start_at=%s", start_at) token_no = start_at result = {} current_key = None while token_no < len(tokens): token = tokens[token_no] log.debug("PROCESSING TOKEN %d: %s", token_no, token) if __is_quoted_string(token): log.debug(" TYPE: QUOTED STRING ") if current_key is None: current_key = __get_quoted_string(token) log.debug(" KEY: %s", current_key) else: result[current_key] = __get_quoted_string(token) log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_datatype(token): log.debug(" TYPE: DATATYPE: %s ", token) result[current_key] = __get_datatype(token) log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_boolean(token): log.debug(" TYPE: BOOLEAN ") result[current_key] = __get_boolean(token) log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_int(token): log.debug(" TYPE: INT ") result[current_key] = __get_int(token) log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_long(token): log.debug(" TYPE: LONG ") result[current_key] = __get_long(token) log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_undefined(token): log.debug(" TYPE: UNDEFINED ") log.debug(" %s -> undefined (Adding as None to map)", current_key) result[current_key] = None current_key = None elif __is_dict_start(token): log.debug(" TYPE: DICT START") dict_value, token_no = __process_tokens_internal( tokens, start_at=token_no + 1 ) log.debug(" DICT = %s ", dict_value) result[current_key] = dict_value log.debug(" %s -> %s", current_key, result[current_key]) current_key = None elif __is_dict_end(token): log.debug(" TYPE: DICT END") return result, token_no elif __is_assignment(token): log.debug(" TYPE: ASSIGNMENT") is_assignment = True elif __is_expression(token): log.debug(" TYPE: EXPRESSION") is_expression = True else: raise CommandExecutionError(f"Unknown token! Token: {token}") token_no = token_no + 1 def __tokenize(cli_output): # add all possible tokens here # \\ means a single backslash here tokens_re = re.compile( r'("(?:[^"\\]|\\"|\\\\)*"|=>|{|}|true|false|undefined|[0-9A-Za-z]+)', re.DOTALL ) tokens = tokens_re.findall(cli_output) log.debug("tokens=%s", tokens) return tokens def __is_dict_start(token): return token == "{" def __is_dict_end(token): return token == "}" def __is_boolean(token): return token == "true" or token == "false" def __get_boolean(token): return token == "true" def __is_int(token): return token.isdigit() def __get_int(token): return int(token) def __is_long(token): return token[0:-1].isdigit() and token[-1] == "L" def __get_long(token): return int(token[0:-1]) def __is_datatype(token): return token in ("INT", "BOOLEAN", "STRING", "OBJECT", "LONG") def __get_datatype(token): return token def __is_undefined(token): return token == "undefined" def __is_quoted_string(token): return token[0] == '"' and token[-1] == '"' def __get_quoted_string(token): result = token[1:-1] # remove quotes result = result.replace( "\\\\", "\\" ) # unescape the output, by default all the string are escaped in the output return result def __is_assignment(token): return token == "=>" def __is_expression(token): return token == "expression"