You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2018/10/11 17:11:19 UTC
[trafficcontrol] 09/21: Pulled logic for process search out of
ATS-specific functionality
This is an automated email from the ASF dual-hosted git repository.
dangogh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit a659e5f3415d82786de68536dad901c5c98a82c0
Author: ocket8888 <Br...@comcast.com>
AuthorDate: Wed Oct 3 15:40:35 2018 +0000
Pulled logic for process search out of ATS-specific functionality
---
.../cdn-in-a-box/ort/traffic_ops_ort/services.py | 165 ++++++++++++++-------
1 file changed, 113 insertions(+), 52 deletions(-)
diff --git a/infrastructure/cdn-in-a-box/ort/traffic_ops_ort/services.py b/infrastructure/cdn-in-a-box/ort/traffic_ops_ort/services.py
index 9d3bdb0..ddd6bc3 100644
--- a/infrastructure/cdn-in-a-box/ort/traffic_ops_ort/services.py
+++ b/infrastructure/cdn-in-a-box/ort/traffic_ops_ort/services.py
@@ -24,21 +24,24 @@ for a new service, all that need be done is to write a function to perform confi
reloads, then add the names of configuration files to the map, pointing at the new function.
"""
+import logging
+import os
import subprocess
+import typing
import psutil
#: Holds the list of reloads needed due to configuration file changes
NEEDED_RELOADS = set()
-#: A big ol' map of filenames to the services which require reloads when said files change
-FILES_THAT_REQUIRE_RELOADS = {"records.config": reloadATSConfigs,
- "remap.config": reloadATSConfigs,
- "parent.config": reloadATSConfigs,
- "cache.config": reloadATSConfigs,
- "hosting.config": reloadATSConfigs,
- "astats.config": reloadATSConfigs,
- "logs_xml.config": reloadATSConfigs,
- "ssl_multicert.config": reloadATSConfigs}
+#: True if the host system has systemd D-Bus - actual value set at runtime
+HAS_SYSTEMD = False
+
+try:
+ output = subprocess.check_output(["systemctl", "--no-pager"], stderr=subprocess.STDOUT)
+except subprocess.CalledProcessError:
+ logging.debug("Host system does NOT have systemd - stack trace:", exc_info=True,stack_info=True)
+else:
+ HAS_SYSTEMD = True
def reloadATSConfigs() -> bool:
"""
@@ -49,23 +52,28 @@ def reloadATSConfigs() -> bool:
``traffic_ctl``)
:raises OSError: when something goes wrong executing the child process
"""
- from .configuration import MODE, TS_ROOT
+ from .configuration import MODE, Modes, TS_ROOT
from .utils import getYesNoResponse as getYN
+ # First of all, ATS must be running for this to work
+ if not setATSStatus(True):
+ logging.error("Cannot reload configs, ATS not running!")
+ return False
+
cmd = [os.path.join(TS_ROOT, "bin", "traffic_ctl"), "config", "reload"]
cmdStr = ' '.join(cmd)
- if MODE == MODE.INTERACTIVE and not getYN("Run command '%s' to reload configuration?",cmdStr):
+ if MODE is Modes.INTERACTIVE and not getYN("Run command '%s' to reload configuration?",cmdStr):
logging.warning("Configuration will not be reloaded for Apache Trafficserver!")
logging.warning("Changes will NOT be applied!")
return True
logging.info("Apache Trafficserver configuration reload will be done via: %s", cmdStr)
- if MODE == MODE.REPORT:
+ if MODE is Modes.REPORT:
return True
- sub = subprocess.Popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ sub = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = sub.communicate()
if sub.returncode:
@@ -77,6 +85,16 @@ def reloadATSConfigs() -> bool:
return False
return True
+#: A big ol' map of filenames to the services which require reloads when said files change
+FILES_THAT_REQUIRE_RELOADS = {"records.config": reloadATSConfigs,
+ "remap.config": reloadATSConfigs,
+ "parent.config": reloadATSConfigs,
+ "cache.config": reloadATSConfigs,
+ "hosting.config": reloadATSConfigs,
+ "astats.config": reloadATSConfigs,
+ "logs_xml.config": reloadATSConfigs,
+ "ssl_multicert.config": reloadATSConfigs}
+
def doReloads() -> bool:
"""
Performs all necessary service restarts/configuration reloads
@@ -96,6 +114,30 @@ def doReloads() -> bool:
return True
+def getProcessesIfRunning(name:str) -> typing.Optional[psutil.Process]:
+ """
+ Retrieves a process by name, if it exists.
+
+ .. warning:: Process names don't have to be unique, this will return the process with the
+ lowest PID that matches ``name``. This can also only return processes visible to the
+ user running the Python interpreter.
+
+ :param name: the name for which to search
+ :returns: a process if one is found that matches ``name``, else :const:`None`
+ :raises OSError: if the process table cannot be iterated
+ """
+ logging.debug("Iterating process list - looking for %s", name)
+ for process in psutil.process_iter():
+
+ # Found
+ if process.name() == name:
+ logging.debug("Running process found (pid: %d)", process.pid)
+ return process
+
+ logging.debug("No process named '%s' was found", name)
+
+ return None
+
def setATSStatus(status:bool, restart:bool = False) -> bool:
"""
Sets the status of the system's ATS process.
@@ -108,49 +150,49 @@ def setATSStatus(status:bool, restart:bool = False) -> bool:
:returns: whether or not the status setting was successful (or unnecessary)
:raises OSError: when there is a problem executing the subprocess
"""
- from .configuration import MODE, TS_ROOT
+ from .configuration import MODE, Modes, TS_ROOT
from .utils import getYesNoResponse as getYN
- logging.debug("Iterating process list")
+ existingProcess = getProcessesIfRunning("[TS_MAIN]")
- arg = None
- for process in psutil.process_iter():
+ # ATS is not running
+ if existingProcess is None:
+ if not status:
+ logging.info("ATS already stopped - nothing to do")
+ return True
+ logging.info("ATS not running, will be started")
+ arg = "start"
- # Found an ATS process
- if process.name() == "[TS_MAIN]":
- logging.debug("ATS process found (pid: %d)", process.pid)
- ATSAlreadyRunning = process.status() in {psutil.STATUS_RUNNING, psutil.STATUS_SLEEPING}
-
- if status and ATSAlreadyRunning and restart:
- logging.info("ATS process found; restarting")
- arg = "restart"
- elif status and ATSAlreadyRunning:
- logging.info("ATS already running; nothing to do.")
- return True
- elif status:
- logging.warning("ATS process is running, but status is '%s' - restarting", process.status())
- arg = "restart"
- else:
- logging.warning("ATS is running; stopping ATS")
- arg = "stop"
-
- break
+
+ # ATS is running, but has a bad status
+ elif status and existingProcess.status() not in {psutil.STATUS_RUNNING, psutil.STATUS_SLEEPING}:
+ logging.warning("ATS already running, but status is %s - restarting",
+ existingProcess.status())
+ arg = "restart"
+
+ # ATS is running and should be stopped
+ elif not status:
+ logging.info("ATS is running, will be stopped")
+ arg = "stop"
+
+ # ATS is running fine, but we want to restart it
+ elif restart:
+ logging.info("ATS process found - restarting")
+ arg = "restart"
+
+ # ATS is running fine already
else:
- if status:
- logging.warning("ATS not already running; starting ATS.")
- arg = "start"
- else:
- logging.info("ATS already not running; nothing to do.")
- return True
+ logging.info("ATS already running - nothing to do")
+ return True
tsexe = os.path.join(TS_ROOT, "bin", "trafficserver")
- if MODE == MODE.INTERACTIVE and not getYN("Run command '%s %s'?" % (tsexe, arg)):
+ if MODE is Modes.INTERACTIVE and not getYN("Run command '%s %s'?" % (tsexe, arg)):
logging.warning("ATS status will not be set - Traffic Ops may not expect this!")
return True
logging.info("ATS status will be set using: %s %s", tsexe, arg)
- if MODE != MODE.REPORT:
+ if MODE is not Modes.REPORT:
sub = subprocess.Popen([tsexe, arg], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = sub.communicate()
@@ -175,19 +217,38 @@ def setServiceStatus(chkconfig:dict) -> bool:
:param chkconfig: A single chkconfig
:returns: whether or not the service's status was set successfully
"""
+ global HAS_SYSTEMD
+ from .utils import getYesNoResponse as getYN
+ from .configuration import MODE, Modes
+
+ if not HAS_SYSTEMD:
+ logging.warning("This system doesn't have systemd, services cannot be enabled/disabled")
+ return True
try:
- if chkconfig['name'] == "trafficserver":
- if not setATSStatus(on in chkconfig["value"]):
- logging.critical("Failed to set Apache Trafficserver status!")
- return False
+ status = "enable" if "on" in chkconfig.value else "disable"
+ service = chkconfig['name']
except KeyError as e:
logging.error("'%r' could not be parsed as a chkconfig object!", chkconfig)
logging.debug("%s", e, exc_info=True, stack_info=True)
return False
- except OSError as e:
- logging.error("An error occurred when setting Apache Trafficserver status: %s", e)
- logging.debug("%s", e, exc_info=True, stack_info=True)
- return False
+
+ if MODE is Modes.INTERACTIVE and not getYN("%s %s?" % (service, status), default='Y'):
+ logging.warning("%s will not be %sd - some things may break!", service, status)
+ return True
+
+ logging.info("%s will be %sd", service, status)
+
+ if MODE is not Modes.REPORT:
+ try:
+ sub = subprocess.Popen(["systemctl", status, service],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out, err = sub.communicate()
+ logging.debug("output")
+ except (OSError, subprocess.CalledProcessError) as e:
+ logging.error("An error occurred when %sing %s: %s", status[:-1], service, e)
+ logging.debug("%r", e, exc_info=True, stack_info=True)
+ return False
return True