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/tomcat.py |
""" Support for Tomcat This module uses the manager webapp to manage Apache tomcat webapps. If the manager webapp is not configured some of the functions won't work. :configuration: - Java bin path should be in default path - If ipv6 is enabled make sure you permit manager access to ipv6 interface "0:0:0:0:0:0:0:1" - If you are using tomcat.tar.gz it has to be installed or symlinked under ``/opt``, preferably using name tomcat - "tomcat.signal start/stop" works but it does not use the startup scripts The following grains/pillar should be set: .. code-block:: yaml tomcat-manager: user: <username> passwd: <password> or the old format: .. code-block:: yaml tomcat-manager.user: <username> tomcat-manager.passwd: <password> Also configure a user in the conf/tomcat-users.xml file: .. code-block:: xml <?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager-script"/> <user username="tomcat" password="tomcat" roles="manager-script"/> </tomcat-users> .. note:: - More information about tomcat manager: http://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html - if you use only this module for deployments you've might want to strict access to the manager only from localhost for more info: http://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html#Configuring_Manager_Application_Access - Tested on: JVM Vendor: Sun Microsystems Inc. JVM Version: 1.6.0_43-b01 OS Architecture: amd64 OS Name: Linux OS Version: 2.6.32-358.el6.x86_64 Tomcat Version: Apache Tomcat/7.0.37 """ import glob import hashlib import logging import os import re import tempfile import urllib.parse import urllib.request import salt.utils.data import salt.utils.stringutils log = logging.getLogger(__name__) __func_alias__ = {"reload_": "reload"} # Support old-style grains/pillar # config as well as new. __valid_configs = { "user": ["tomcat-manager.user", "tomcat-manager:user"], "passwd": ["tomcat-manager.passwd", "tomcat-manager:passwd"], } def __virtual__(): """ Only load tomcat if it is installed or if grains/pillar config exists """ if __catalina_home() or _auth("dummy"): return "tomcat" return ( False, "Tomcat execution module not loaded: neither Tomcat installed locally nor" " tomcat-manager credentials set in grains/pillar/config.", ) def __catalina_home(): """ Tomcat paths differ depending on packaging """ locations = ["/usr/share/tomcat*", "/opt/tomcat"] for location in locations: folders = glob.glob(location) if folders: for catalina_home in folders: if os.path.isdir(catalina_home + "/bin"): return catalina_home return False def _get_credentials(): """ Get the username and password from opts, grains, or pillar """ ret = {"user": False, "passwd": False} # Loop through opts, grains, and pillar # Return the first acceptable configuration found for item in ret: for struct in [__opts__, __grains__, __pillar__]: # Look for the config key # Support old-style config format and new for config_key in __valid_configs[item]: value = salt.utils.data.traverse_dict_and_list(struct, config_key, None) if value: ret[item] = value break return ret["user"], ret["passwd"] def _auth(uri): """ returns a authentication handler. Get user & password from grains, if are not set default to modules.config.option If user & pass are missing return False """ user, password = _get_credentials() if user is False or password is False: return False basic = urllib.request.HTTPBasicAuthHandler() basic.add_password( realm="Tomcat Manager Application", uri=uri, user=user, passwd=password ) digest = urllib.request.HTTPDigestAuthHandler() digest.add_password( realm="Tomcat Manager Application", uri=uri, user=user, passwd=password ) return urllib.request.build_opener(basic, digest) def extract_war_version(war): """ Extract the version from the war file name. There does not seem to be a standard for encoding the version into the `war file name`_ .. _`war file name`: https://tomcat.apache.org/tomcat-6.0-doc/deployer-howto.html Examples: .. code-block:: bash /path/salt-2015.8.6.war -> 2015.8.6 /path/V6R2013xD5.war -> None """ basename = os.path.basename(war) war_package = os.path.splitext(basename)[0] # remove '.war' version = re.findall("-([\\d.-]+)$", war_package) # try semver return version[0] if version and len(version) == 1 else None # default to none def _wget(cmd, opts=None, url="http://localhost:8080/manager", timeout=180): """ A private function used to issue the command to tomcat via the manager webapp cmd the command to execute url The URL of the server manager webapp (example: http://localhost:8080/manager) opts a dict of arguments timeout timeout for HTTP request Return value is a dict in the from of:: { res: [True|False] msg: list of lines we got back from the manager } """ ret = {"res": True, "msg": []} # prepare authentication auth = _auth(url) if auth is False: ret["res"] = False ret["msg"] = "missing username and password settings (grain/pillar)" return ret # prepare URL if url[-1] != "/": url += "/" url6 = url url += f"text/{cmd}" url6 += f"{cmd}" if opts: url += f"?{urllib.parse.urlencode(opts)}" url6 += f"?{urllib.parse.urlencode(opts)}" # Make the HTTP request urllib.request.install_opener(auth) try: # Trying tomcat >= 7 url ret["msg"] = urllib.request.urlopen(url, timeout=timeout).read().splitlines() except Exception: # pylint: disable=broad-except try: # Trying tomcat6 url ret["msg"] = ( urllib.request.urlopen(url6, timeout=timeout).read().splitlines() ) except Exception: # pylint: disable=broad-except ret["msg"] = "Failed to create HTTP request" # Force all byte strings to utf-8 strings, for python >= 3.4 for key, value in enumerate(ret["msg"]): try: ret["msg"][key] = salt.utils.stringutils.to_unicode(value, "utf-8") except (UnicodeDecodeError, AttributeError): pass if not ret["msg"][0].startswith("OK"): ret["res"] = False return ret def _simple_cmd(cmd, app, url="http://localhost:8080/manager", timeout=180): """ Simple command wrapper to commands that need only a path option """ try: opts = {"path": app, "version": ls(url)[app]["version"]} return "\n".join(_wget(cmd, opts, url, timeout=timeout)["msg"]) except Exception: # pylint: disable=broad-except return f"FAIL - No context exists for path {app}" # Functions def leaks(url="http://localhost:8080/manager", timeout=180): """ Find memory leaks in tomcat url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.leaks """ return _wget("findleaks", {"statusLine": "true"}, url, timeout=timeout)["msg"] def status(url="http://localhost:8080/manager", timeout=180): """ Used to test if the tomcat manager is up url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.status salt '*' tomcat.status http://localhost:8080/manager """ return _wget("list", {}, url, timeout=timeout)["res"] def ls(url="http://localhost:8080/manager", timeout=180): """ list all the deployed webapps url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.ls salt '*' tomcat.ls http://localhost:8080/manager """ ret = {} data = _wget("list", "", url, timeout=timeout) if data["res"] is False: return {} data["msg"].pop(0) for line in data["msg"]: tmp = line.split(":") ret[tmp[0]] = { "mode": tmp[1], "sessions": tmp[2], "fullname": tmp[3], "version": "", } sliced = tmp[3].split("##") if len(sliced) > 1: ret[tmp[0]]["version"] = sliced[1] return ret def stop(app, url="http://localhost:8080/manager", timeout=180): """ Stop the webapp app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.stop /jenkins salt '*' tomcat.stop /jenkins http://localhost:8080/manager """ return _simple_cmd("stop", app, url, timeout=timeout) def start(app, url="http://localhost:8080/manager", timeout=180): """ Start the webapp app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.start /jenkins salt '*' tomcat.start /jenkins http://localhost:8080/manager """ return _simple_cmd("start", app, url, timeout=timeout) def reload_(app, url="http://localhost:8080/manager", timeout=180): """ Reload the webapp app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.reload /jenkins salt '*' tomcat.reload /jenkins http://localhost:8080/manager """ return _simple_cmd("reload", app, url, timeout=timeout) def sessions(app, url="http://localhost:8080/manager", timeout=180): """ return the status of the webapp sessions app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.sessions /jenkins salt '*' tomcat.sessions /jenkins http://localhost:8080/manager """ return _simple_cmd("sessions", app, url, timeout=timeout) def status_webapp(app, url="http://localhost:8080/manager", timeout=180): """ return the status of the webapp (stopped | running | missing) app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.status_webapp /jenkins salt '*' tomcat.status_webapp /jenkins http://localhost:8080/manager """ webapps = ls(url, timeout=timeout) for i in webapps: if i == app: return webapps[i]["mode"] return "missing" def serverinfo(url="http://localhost:8080/manager", timeout=180): """ return details about the server url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.serverinfo salt '*' tomcat.serverinfo http://localhost:8080/manager """ data = _wget("serverinfo", {}, url, timeout=timeout) if data["res"] is False: return {"error": data["msg"]} ret = {} data["msg"].pop(0) for line in data["msg"]: tmp = line.split(":") ret[tmp[0].strip()] = tmp[1].strip() return ret def undeploy(app, url="http://localhost:8080/manager", timeout=180): """ Undeploy a webapp app the webapp context path url : http://localhost:8080/manager the URL of the server manager webapp timeout : 180 timeout for HTTP request CLI Examples: .. code-block:: bash salt '*' tomcat.undeploy /jenkins salt '*' tomcat.undeploy /jenkins http://localhost:8080/manager """ return _simple_cmd("undeploy", app, url, timeout=timeout) def deploy_war( war, context, force="no", url="http://localhost:8080/manager", saltenv="base", timeout=180, temp_war_location=None, version=True, ): """ Deploy a WAR file war absolute path to WAR file (should be accessible by the user running tomcat) or a path supported by the salt.modules.cp.get_file function context the context path to deploy force : False set True to deploy the webapp even one is deployed in the context url : http://localhost:8080/manager the URL of the server manager webapp saltenv : base the environment for WAR file in used by salt.modules.cp.get_url function timeout : 180 timeout for HTTP request temp_war_location : None use another location to temporarily copy to war file by default the system's temp directory is used version : '' Specify the war version. If this argument is provided, it overrides the version encoded in the war file name, if one is present. Examples: .. code-block:: bash salt '*' tomcat.deploy_war salt://salt-2015.8.6.war version=2015.08.r6 .. versionadded:: 2015.8.6 CLI Examples: cp module .. code-block:: bash salt '*' tomcat.deploy_war salt://application.war /api salt '*' tomcat.deploy_war salt://application.war /api no salt '*' tomcat.deploy_war salt://application.war /api yes http://localhost:8080/manager minion local file system .. code-block:: bash salt '*' tomcat.deploy_war /tmp/application.war /api salt '*' tomcat.deploy_war /tmp/application.war /api no salt '*' tomcat.deploy_war /tmp/application.war /api yes http://localhost:8080/manager """ # Decide the location to copy the war for the deployment tfile = f"salt.{os.path.basename(war)}" if temp_war_location is not None: if not os.path.isdir(temp_war_location): return f'Error - "{temp_war_location}" is not a directory' tfile = os.path.join(temp_war_location, tfile) else: tfile = os.path.join(tempfile.gettempdir(), tfile) # Copy file name if needed cache = False if not os.path.isfile(war): cache = True cached = __salt__["cp.get_url"](war, tfile, saltenv) if not cached: return "FAIL - could not cache the WAR file" try: __salt__["file.set_mode"](cached, "0644") except KeyError: pass else: tfile = war # Prepare options opts = { "war": f"file:{tfile}", "path": context, } # If parallel versions are desired or not disabled if version: # Set it to defined version or attempt extract version = extract_war_version(war) if version is True else version if isinstance(version, str): # Only pass version to Tomcat if not undefined opts["version"] = version if force == "yes": opts["update"] = "true" # Deploy deployed = _wget("deploy", opts, url, timeout=timeout) res = "\n".join(deployed["msg"]) # Cleanup if cache: __salt__["file.remove"](tfile) return res def passwd(passwd, user="", alg="sha1", realm=None): """ This function replaces the $CATALINA_HOME/bin/digest.sh script convert a clear-text password to the $CATALINA_BASE/conf/tomcat-users.xml format CLI Examples: .. code-block:: bash salt '*' tomcat.passwd secret salt '*' tomcat.passwd secret tomcat sha1 salt '*' tomcat.passwd secret tomcat sha1 'Protected Realm' """ # pylint: disable=no-value-for-parameter # we call the first parameter the same as the function! # Shouldn't it be SHA265 instead of SHA1? digest = getattr(hashlib, alg, None) if digest is not None: if realm: digest.update(f"{user}:{realm}:{passwd}") else: digest.update(passwd) return digest and digest.hexdigest() or False # Non-Manager functions def version(): """ Return server version from catalina.sh version CLI Example: .. code-block:: bash salt '*' tomcat.version """ cmd = __catalina_home() + "/bin/catalina.sh version" out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue if "Server version" in line: comps = line.split(": ") return comps[1] def fullversion(): """ Return all server information from catalina.sh version CLI Example: .. code-block:: bash salt '*' tomcat.fullversion """ cmd = __catalina_home() + "/bin/catalina.sh version" ret = {} out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue if ": " in line: comps = line.split(": ") ret[comps[0]] = comps[1].lstrip() return ret def signal(signal=None): """ Signals catalina to start, stop, securestart, forcestop. CLI Example: .. code-block:: bash salt '*' tomcat.signal start """ valid_signals = { "forcestop": "stop -force", "securestart": "start -security", "start": "start", "stop": "stop", } if signal not in valid_signals: return cmd = f"{__catalina_home()}/bin/catalina.sh {valid_signals[signal]}" __salt__["cmd.run"](cmd) if __name__ == "__main__": # Allow testing from the CLI __opts__ = {} __grains__ = {} __pillar__ = { "tomcat-manager.user": "foobar", "tomcat-manager.passwd": "barfoo1!", } old_format_creds = _get_credentials() __pillar__ = {"tomcat-manager": {"user": "foobar", "passwd": "barfoo1!"}} new_format_creds = _get_credentials() if old_format_creds == new_format_creds: log.info("Config backwards compatible") else: log.ifno("Config not backwards compatible")