Skip to content
Snippets Groups Projects
test_wowza.py 5.22 KiB
Newer Older
#!/usr/bin/env python3
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed

"""
Criticality: Normal
Checks that the streaming server (Wowza) is running correctly.
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
"""

from pathlib import Path
import re
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
import subprocess  # nosec: B404
import sys
import psutil
from defusedxml.ElementTree import parse
from psutil import net_connections
from packaging.version import parse as parse_version
from lxml import etree
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
sys.path.append(str(Path(__file__).parents[1].resolve()))

# pylint: disable=wrong-import-position
from utilities import logging as lg  # noqa: E402
LATEST_VERSION = "4.8.5"
WOWZA_TUNE_FILE = "/usr/local/WowzaStreamingEngine/conf/Tune.xml"
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
def main():
    """Run all checks and exits with corresponding exit code."""

    print("Checking Wowza settings:")
    warnings = 0
    errors = 0

    # check if wowza is installed
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    if not check_installed():
        exit(2)

    # check wowza version
    check_warn, check_err = check_version()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check wowza heap size
    check_warn, check_err = check_heap_size()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check that wowza is running
    check_warn, check_err = check_running()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

    # check that wowza is listening
    check_warn, check_err = check_listening()
    if check_err:
        errors += 1
    elif check_warn:
        warnings += 1

Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    if errors:
        exit(1)
    elif warnings:
        exit(3)

    exit(0)


def check_installed() -> bool:
    """Check that Wowza is installed.

    :return: Exit return codes
    :rtype: bool
    """

    cmd = "dpkg --get-selections 'wowza*'"
    out = subprocess.getoutput(cmd)
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    state = out.split()[-1]
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    if state != "install":
        lg.info("not installed, skip test")
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
        return False

    return True


def check_version() -> tuple:
    """Check the Wowza version installed.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    cmd = "dpkg --get-selections 'wowza*'"
    out = subprocess.getoutput(cmd)
    version = None
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed

    for line in out.split("\n"):
        if line.split()[-1] == "install":
            if version:
                lg.error("many Wowza versions installed, keep only latest")
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
                errors += 1
            version = ".".join(re.findall(r"\d", line))

    if not version:
        lg.error("cannot find Wowza version")
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
        errors += 1

    if parse_version(version) < parse_version(LATEST_VERSION):
        lg.info(
            "using outdated version: {0} < {1} (recommended)"
            .format(version, LATEST_VERSION)
        )
    elif parse_version(version) > parse_version(LATEST_VERSION):
        lg.success(
            "using newer version than the recommended: {0} > {1} (recommended)"
            .format(version, LATEST_VERSION)
        )
        lg.success("using the recommended version: {0}".format(version))
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    return warnings, errors


def check_heap_size() -> tuple:
    """Check the heap size configured.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    # Current total RAM extraction (in MB)
    svmem = psutil.virtual_memory()
    total_ram = round(svmem.total / 1024 / 1024)

    # Configuration of the recommended wowza heap size regarding available RAM
    if total_ram >= 15000:
        recommended_heap_size = 8000
    elif total_ram >= 7000:
        recommended_heap_size = 4000
    else:
        recommended_heap_size = 2000

    # Configured wowza heap size extraction
    tune_xml = etree.parse(WOWZA_TUNE_FILE)
    heap_size = tune_xml.find('Tune/HeapSize').text[0:-1]

    if int(heap_size) < recommended_heap_size:
        lg.warning(
            "not using recommended heap size: {0}M < {1}M (recommended)"
            .format(int(heap_size), recommended_heap_size)
        )
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
        warnings += 1
        lg.success(
            "using recommended heap size or above: {0}M"
            .format(int(heap_size))
        )
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
    return warnings, errors


def check_running() -> tuple:
    """Check that Wowza is running.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    cmd = "systemctl status WowzaStreamingEngine"
    out = subprocess.getoutput(cmd)
    if "Active: active (running)" not in out:
        lg.error("service not running")
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
        errors += 1
        lg.success("service running")
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed

    return warnings, errors
def check_listening() -> tuple:
    """Check that Wowza is listening on configured port.

    :return: Exit return codes
    :rtype: bool
    """

    warnings = 0
    errors = 0

    # get port number in Wowza config
    conf = parse("/usr/local/WowzaStreamingEngine/conf/VHost.xml").getroot()
    port = conf.findall("VHost/HostPortList/HostPort/Port")[0].text

    # get listening ports
    listening = set(
        c.laddr[1] for c in net_connections(kind="inet") if c.status == "LISTEN"
    )

    # check that system is listening on this port
    if int(port) not in listening:
        lg.error("not listening on port {}".format(port))
        lg.success("listening on port {}".format(port))
Nicolas KAROLAK's avatar
Nicolas KAROLAK committed
if __name__ == "__main__":
    main()