Source code for saltext.apache.modules.apache

"""
Support for Apache

.. note::
    The functions in here are generic functions designed to work with
    all implementations of Apache. Debian-specific functions have been moved into
    deb_apache.py, but will still load under the ``apache`` namespace when a
    Debian-based system is detected.
"""

import io
import logging
import re
import urllib.error
import urllib.request

import salt.utils.data
import salt.utils.files
import salt.utils.path
import salt.utils.stringutils
from salt.exceptions import SaltException

log = logging.getLogger(__name__)


[docs] def __virtual__(): """ Only load the module if apache is installed """ cmd = _detect_os() if salt.utils.path.which(cmd): return "apache" return ( False, "The apache execution module cannot be loaded: apache is not installed.", )
def _detect_os(): """ Apache commands and paths differ depending on packaging """ # TODO: Add pillar support for the apachectl location os_family = __grains__["os_family"] if os_family == "RedHat": return "apachectl" if os_family in ("Debian", "Suse"): return "apache2ctl" return "apachectl"
[docs] def version(): """ Return server version (``apachectl -v``) CLI Example: .. code-block:: bash salt '*' apache.version """ cmd = f"{_detect_os()} -v" out = __salt__["cmd.run"](cmd).splitlines() ret = out[0].split(": ") return ret[1]
[docs] def fullversion(): """ Return server version (``apachectl -V``) CLI Example: .. code-block:: bash salt '*' apache.fullversion """ cmd = f"{_detect_os()} -V" ret = {} ret["compiled_with"] = [] out = __salt__["cmd.run"](cmd).splitlines() # Example # -D APR_HAS_MMAP define_re = re.compile(r"^\s+-D\s+") for line in out: if ": " in line: comps = line.split(": ") if not comps: continue ret[comps[0].strip().lower().replace(" ", "_")] = comps[1].strip() elif " -D" in line: cwith = define_re.sub("", line) ret["compiled_with"].append(cwith) return ret
[docs] def modules(): """ Return list of static and shared modules (``apachectl -M``) CLI Example: .. code-block:: bash salt '*' apache.modules """ cmd = f"{_detect_os()} -M" ret = {} ret["static"] = [] ret["shared"] = [] out = __salt__["cmd.run"](cmd).splitlines() for line in out: comps = line.split() if not comps: continue if "(static)" in line: ret["static"].append(comps[0]) if "(shared)" in line: ret["shared"].append(comps[0]) return ret
[docs] def servermods(): """ Return list of modules compiled into the server (``apachectl -l``) CLI Example: .. code-block:: bash salt '*' apache.servermods """ cmd = f"{_detect_os()} -l" ret = [] out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue if ".c" in line: ret.append(line.strip()) return ret
[docs] def directives(): """ Return list of directives together with expected arguments and places where the directive is valid (``apachectl -L``) CLI Example: .. code-block:: bash salt '*' apache.directives """ cmd = f"{_detect_os()} -L" ret = {} out = __salt__["cmd.run"](cmd) out = out.replace("\n\t", "\t") for line in out.splitlines(): if not line: continue comps = line.split("\t") desc = "\n".join(comps[1:]) ret[comps[0]] = desc return ret
[docs] def vhosts(): """ Show the settings as parsed from the config file (currently only shows the virtualhost settings) (``apachectl -S``). Because each additional virtual host adds to the execution time, this command may require a long timeout be specified by using ``-t 10``. CLI Example: .. code-block:: bash salt -t 10 '*' apache.vhosts """ cmd = f"{_detect_os()} -S" ret = {} namevhost = "" out = __salt__["cmd.run"](cmd) for line in out.splitlines(): if not line: continue comps = line.split() if "is a NameVirtualHost" in line: namevhost = comps[0] ret[namevhost] = {} else: if comps[0] == "default": ret[namevhost]["default"] = {} ret[namevhost]["default"]["vhost"] = comps[2] ret[namevhost]["default"]["conf"] = re.sub(r"\(|\)", "", comps[3]) if comps[0] == "port": ret[namevhost][comps[3]] = {} ret[namevhost][comps[3]]["vhost"] = comps[3] ret[namevhost][comps[3]]["conf"] = re.sub(r"\(|\)", "", comps[4]) ret[namevhost][comps[3]]["port"] = comps[1] return ret
# pylint: disable=redefined-outer-name
[docs] def signal(signal=None): """ Signals httpd to start, restart, or stop. CLI Example: .. code-block:: bash salt '*' apache.signal restart """ no_extra_args = ("configtest", "status", "fullstatus") valid_signals = ("start", "stop", "restart", "graceful", "graceful-stop") if signal not in valid_signals and signal not in no_extra_args: return None # Make sure you use the right arguments if signal in valid_signals: arguments = f" -k {signal}" else: arguments = f" {signal}" cmd = _detect_os() + arguments out = __salt__["cmd.run_all"](cmd) # A non-zero return code means fail if out["retcode"] and out["stderr"]: ret = out["stderr"].strip() # 'apachectl configtest' returns 'Syntax OK' to stderr elif out["stderr"]: ret = out["stderr"].strip() elif out["stdout"]: ret = out["stdout"].strip() # No output for something like: apachectl graceful else: ret = f'Command: "{cmd}" completed successfully!' return ret
[docs] def useradd(pwfile, user, password, opts=""): """ Add HTTP user using the ``htpasswd`` command. If the ``htpasswd`` file does not exist, it will be created. Valid options that can be passed are: .. code-block:: text n Don't update file; display results on stdout. m Force MD5 hashing of the password (default). d Force CRYPT(3) hashing of the password. p Do not hash the password (plaintext). s Force SHA1 hashing of the password. CLI Examples: .. code-block:: bash salt '*' apache.useradd /etc/httpd/htpasswd larry badpassword salt '*' apache.useradd /etc/httpd/htpasswd larry badpass opts=ns """ return __salt__["webutil.useradd"](pwfile, user, password, opts)
[docs] def userdel(pwfile, user): """ Delete HTTP user from the specified ``htpasswd`` file. CLI Example: .. code-block:: bash salt '*' apache.userdel /etc/httpd/htpasswd larry """ return __salt__["webutil.userdel"](pwfile, user)
[docs] def server_status(profile="default"): """ Get Information from the Apache server-status handler .. note:: The server-status handler is disabled by default. In order for this function to work it needs to be enabled. See http://httpd.apache.org/docs/2.2/mod/mod_status.html The following configuration needs to exists in pillar/grains. Each entry nested in ``apache.server-status`` is a profile of a vhost/server. This would give support for multiple apache servers/vhosts. .. code-block:: yaml apache.server-status: default: url: http://localhost/server-status user: someuser pass: password realm: 'authentication realm for digest passwords' timeout: 5 CLI Examples: .. code-block:: bash salt '*' apache.server_status salt '*' apache.server_status other-profile """ ret = { "Scoreboard": { "_": 0, "S": 0, "R": 0, "W": 0, "K": 0, "D": 0, "C": 0, "L": 0, "G": 0, "I": 0, ".": 0, }, } # Get configuration from pillar url = __salt__["config.get"]( f"apache.server-status:{profile}:url", "http://localhost/server-status" ) user = __salt__["config.get"](f"apache.server-status:{profile}:user", "") passwd = __salt__["config.get"](f"apache.server-status:{profile}:pass", "") realm = __salt__["config.get"](f"apache.server-status:{profile}:realm", "") timeout = __salt__["config.get"](f"apache.server-status:{profile}:timeout", 5) # create authentication handler if configuration exists if user and passwd: basic = urllib.request.HTTPBasicAuthHandler() basic.add_password(realm=realm, uri=url, user=user, passwd=passwd) digest = urllib.request.HTTPDigestAuthHandler() digest.add_password(realm=realm, uri=url, user=user, passwd=passwd) urllib.request.install_opener(urllib.request.build_opener(basic, digest)) # get http data url += "?auto" try: with urllib.request.urlopen(url, timeout=timeout) as http_response: response = http_response.read().splitlines() except urllib.error.URLError: return "error" # parse the data for line in response: splt = line.split(":", 1) splt[0] = splt[0].strip() splt[1] = splt[1].strip() if splt[0] == "Scoreboard": for c in splt[1]: ret["Scoreboard"][c] += 1 else: if splt[1].isdigit(): ret[splt[0]] = int(splt[1]) else: ret[splt[0]] = float(splt[1]) # return the good stuff return ret
def _parse_config(conf, slot=None): """ Recursively goes through config structure and builds final Apache configuration :param conf: defined config structure :param slot: name of section container if needed """ ret = io.StringIO() if isinstance(conf, str): if slot: print(f"{slot} {conf}", file=ret, end="") else: print(f"{conf}", file=ret, end="") elif isinstance(conf, list): is_section = False slot_this = None for item in conf: if "this" in item: is_section = True slot_this = str(item["this"]) if is_section: print(f"<{slot} {slot_this}>", file=ret) for item in conf: for key, val in item.items(): if key != "this": print(_parse_config(val, str(key)), file=ret) print(f"</{slot}>", file=ret) else: for value in conf: print(_parse_config(value, str(slot)), file=ret) elif isinstance(conf, dict): try: print(f"<{slot} {conf['this']}>", file=ret) except KeyError as err: raise SaltException( f'Apache section container "<{slot}>" expects attribute. ' 'Specify it using key "this".' ) from err for key, value in conf.items(): if key != "this": if isinstance(value, str): print(f"{key} {value}", file=ret) elif isinstance(value, list): print(_parse_config(value, key), file=ret) elif isinstance(value, dict): print(_parse_config(value, key), file=ret) print(f"</{slot}>", file=ret) ret.seek(0) return ret.read()
[docs] def config(name, config, edit=True): """ Create VirtualHost configuration files name File for the virtual host config VirtualHost configurations .. note:: This function is not meant to be used from the command line. Config is meant to be an ordered dict of all of the apache configs. CLI Example: .. code-block:: bash salt '*' apache.config /etc/httpd/conf.d/ports.conf config="[{'Listen': '22'}]" """ configs = [] for entry in config: key = next(iter(entry.keys())) configs.append(_parse_config(entry[key], key)) # Python auto-correct line endings configstext = "\n".join(salt.utils.data.decode(configs)) if edit: with salt.utils.files.fopen(name, "w") as configfile: configfile.write("# This file is managed by Salt.\n") configfile.write(salt.utils.stringutils.to_str(configstext)) return configstext