Beispiel #1
0
    def _applyConfiguration(self, configuration):
        """Configure the proxy server.

        :param configuration: The configuration object obtained from
            `_getConfiguration`.
        """
        service = service_monitor.getServiceByName(self.service_name)
        if configuration.is_rack and not configuration.is_region:
            if not configuration.enabled:
                # Proxy should be off and remain off.
                service.off("not enabled")
                return service_monitor.ensureService(self.service_name)

            # Proxy enabled; configure it.
            d = deferToThread(self._configure, configuration)
            # Ensure that the service is on.
            service.on()
            if snappy.running_in_snap():
                # XXX: andreserl 2016-05-09 bug=1687620. When running in a
                # snap, supervisord tracks services. It does not support
                # reloading. Instead, we need to restart the service.
                d.addCallback(callOut, service_monitor.restartService,
                              self.service_name)
            else:
                d.addCallback(callOut, service_monitor.reloadService,
                              self.service_name)
            return d
        else:
            # Proxy is managed by the region.
            service.any("managed by the region")
Beispiel #2
0
def sudo_write_file(filename, contents, mode=0o644):
    """Write (or overwrite) file as root.  USE WITH EXTREME CARE.

    Runs an atomic update using non-interactive `sudo`.  This will fail if
    it needs to prompt for a password.

    When running in a snap or devel mode, this function calls
    `atomic_write` directly.

    :type contents: `bytes`.
    """
    from provisioningserver.config import is_dev_environment

    if not isinstance(contents, bytes):
        raise TypeError("Content must be bytes, got: %r" % (contents, ))
    if snappy.running_in_snap():
        atomic_write(contents, filename, mode=mode)
    else:
        maas_write_file = get_library_script_path("maas-write-file")
        command = _with_dev_python(maas_write_file, filename, "%.4o" % mode)
        if not is_dev_environment():
            command = sudo(command)
        proc = Popen(command, stdin=PIPE)
        stdout, stderr = proc.communicate(contents)
        if proc.returncode != 0:
            raise ExternalProcessError(proc.returncode, command, stderr)
Beispiel #3
0
def get_config_v6(template_name: str, global_dhcp_snippets: Sequence[dict],
                  failover_peers: Sequence[dict],
                  shared_networks: Sequence[dict], hosts: Sequence[dict],
                  omapi_key: str) -> str:
    """Return a DHCP config file based on the supplied parameters.

    :param template_name: Template file name: `dhcpd6.conf.template` for the
        IPv6 template.
    :return: A full configuration, as a string.
    """
    platform_codename = linux_distribution()[2]
    template = load_template('dhcp', template_name)
    # Helper functions to stuff into the template namespace.
    helpers = {
        "oneline": normalise_whitespace,
        "commalist": normalise_any_iterable_to_comma_list,
        "quoted_commalist": normalise_any_iterable_to_quoted_comma_list,
        "running_in_snap": snappy.running_in_snap(),
    }

    shared_networks = _process_network_parameters_v6(failover_peers,
                                                     shared_networks)

    try:
        return template.substitute(global_dhcp_snippets=global_dhcp_snippets,
                                   hosts=hosts,
                                   failover_peers=failover_peers,
                                   shared_networks=shared_networks,
                                   platform_codename=platform_codename,
                                   omapi_key=omapi_key,
                                   **helpers)
    except (KeyError, NameError) as error:
        raise DHCPConfigError(
            "Failed to render DHCP configuration.") from error
Beispiel #4
0
def get_dns_default_controls():
    """Include the default RNDC controls (default RNDC key on port 953)?"""
    if running_in_snap():
        # The default controls don't work in a confined snap, since it
        # implicitly requires access to /etc/bind
        return False
    setting = os.getenv("MAAS_DNS_DEFAULT_CONTROLS", "1")
    return (setting == "1")
Beispiel #5
0
def proxy_update_config(reload_proxy=True):
    """Regenerate the proxy configuration file."""
    @transactional
    def write_config():
        allowed_subnets = Subnet.objects.filter(allow_proxy=True)
        cidrs = [subnet.cidr for subnet in allowed_subnets]

        http_proxy = Config.objects.get_config("http_proxy")
        upstream_proxy_enabled = (Config.objects.get_config("use_peer_proxy")
                                  and http_proxy)
        context = {
            'allowed': allowed_subnets,
            'modified': str(datetime.date.today()),
            'fqdn': socket.getfqdn(),
            'cidrs': cidrs,
            'running_in_snap': snappy.running_in_snap(),
            'snap_path': snappy.get_snap_path(),
            'snap_data_path': snappy.get_snap_data_path(),
            'snap_common_path': snappy.get_snap_common_path(),
            'upstream_peer_proxy': upstream_proxy_enabled,
        }

        proxy_enabled = Config.objects.get_config("enable_http_proxy")
        if proxy_enabled and upstream_proxy_enabled:
            http_proxy_hostname = urlparse(http_proxy).hostname
            http_proxy_port = urlparse(http_proxy).port
            context.update({
                'upstream_proxy_address': http_proxy_hostname,
                'upstream_proxy_port': http_proxy_port,
            })

        template_path = locate_template('proxy', MAAS_PROXY_CONF_TEMPLATE)
        template = tempita.Template.from_filename(template_path,
                                                  encoding="UTF-8")
        try:
            content = template.substitute(context)
        except NameError as error:
            raise ProxyConfigFail(*error.args)
        # Squid prefers ascii.
        content = content.encode("ascii")
        target_path = get_proxy_config_path()
        atomic_write(content, target_path, overwrite=True, mode=0o644)

    if is_proxy_enabled():
        d = deferToDatabase(write_config)
        if reload_proxy:
            # XXX: andreserl 2016-05-09 bug=1687620. When running in a snap,
            # supervisord tracks services. It does not support reloading.
            # Instead, we need to restart the service.
            if snappy.running_in_snap():
                d.addCallback(lambda _: service_monitor.restartService(
                    "proxy", if_on=True))
            else:
                d.addCallback(lambda _: service_monitor.reloadService(
                    "proxy", if_on=True))
        return d
    else:
        return succeed(None)
Beispiel #6
0
def get_config_v4(
    template_name: str,
    global_dhcp_snippets: Sequence[dict],
    failover_peers: Sequence[dict],
    shared_networks: Sequence[dict],
    hosts: Sequence[dict],
    omapi_key: str,
) -> str:
    """Return a DHCP config file based on the supplied parameters.

    :param template_name: Template file name: `dhcpd.conf.template` for the
        IPv4 template.
    :return: A full configuration, as a string.
    """
    platform_codename = lsb_release()["codename"]
    template = load_template("dhcp", template_name)
    dhcp_socket = get_maas_data_path("dhcpd.sock")

    # Helper functions to stuff into the template namespace.
    helpers = {
        "oneline": normalise_whitespace,
        "commalist": normalise_any_iterable_to_comma_list,
        "quoted_commalist": normalise_any_iterable_to_quoted_comma_list,
        "running_in_snap": snappy.running_in_snap(),
    }

    for shared_network in shared_networks:
        interface = shared_network.get("interface", None)
        for subnet in shared_network["subnets"]:
            rack_ip = get_rack_ip_for_subnet(
                4, subnet["subnet_cidr"], interface
            )
            if rack_ip is not None:
                subnet["next_server"] = rack_ip
                subnet["bootloader"] = compose_conditional_bootloader(
                    False, rack_ip
                )
            ntp_servers = subnet["ntp_servers"]  # Is a list.
            ntp_servers_ipv4, ntp_servers_ipv6 = _get_addresses(*ntp_servers)
            subnet["ntp_servers_ipv4"] = ", ".join(ntp_servers_ipv4)
            subnet["ntp_servers_ipv6"] = ", ".join(ntp_servers_ipv6)

    try:
        return template.substitute(
            global_dhcp_snippets=global_dhcp_snippets,
            hosts=hosts,
            failover_peers=failover_peers,
            shared_networks=shared_networks,
            platform_codename=platform_codename,
            omapi_key=omapi_key,
            dhcp_helper=(get_path("/usr/sbin/maas-dhcp-helper")),
            dhcp_socket=dhcp_socket,
            **helpers
        )
    except (KeyError, NameError) as error:
        raise DHCPConfigError(
            "Failed to render DHCP configuration."
        ) from error
Beispiel #7
0
def get_version_tuple(maas_version: str) -> MAASVersion:
    without_epoch = maas_version.split(":", 1)[-1]
    version_parts = re.split(r"[-|+]", without_epoch, 1)
    short_version = version_parts[0]
    major_minor_point = re.sub(r"~.*", "", short_version).split(".", 2)
    for i in range(3):
        try:
            major_minor_point[i] = _coerce_to_int(major_minor_point[i])
        except ValueError:
            major_minor_point[i] = 0
        except IndexError:
            major_minor_point.append(0)
    major, minor, point = major_minor_point
    extended_info = ""
    if len(version_parts) > 1:
        extended_info = version_parts[1]
    qualifier_type = None
    qualifier_type_version = 0
    qualifier_version = 0
    if "~" in short_version:
        # Parse the alpha/beta/rc version.
        base_version, qualifier = short_version.split("~", 2)
        qualifier_types = {"rc": -1, "beta": -2, "alpha": -3}
        # A release build won't have a qualifier, so its version should be
        # greater than releases qualified with alpha/beta/rc revisions.
        qualifier_type = re.sub(r"[\d]+", "", qualifier)
        qualifier_version = _coerce_to_int(qualifier)
        qualifier_type_version = qualifier_types.get(qualifier_type, 0)
    revno = 0
    git_rev = ""
    # If we find a '-g' or '.g', that means the extended info indicates a
    # git revision.
    if "-g" in extended_info or ".g" in extended_info:
        # unify separators
        revisions = extended_info.replace("-g.", "-g")
        revno, git_rev = re.split(r"[-|.|+]", revisions)[0:2]
        # Strip any non-numeric characters from the revno, just in case.
        revno = _coerce_to_int(revno)
        # Remove anything that doesn't look like a hexadecimal character.
        git_rev = re.sub(r"[^0-9a-f]+", "", git_rev)
    extended_info = re.sub(r"-*snap$", "", extended_info)
    # Remove unnecessary garbage from the extended info string.
    if "-" in extended_info:
        extended_info = "-".join(extended_info.split("-")[0:2])
    return MAASVersion(
        major,
        minor,
        point,
        qualifier_type_version,
        qualifier_version,
        revno,
        git_rev,
        short_version,
        extended_info,
        qualifier_type,
        snappy.running_in_snap(),
    )
Beispiel #8
0
def create_gnupg_home(tftp_root=None):
    """Upgrade hook: create maas user's GNUPG home directory."""
    gpghome = get_maas_user_gpghome()
    if not os.path.isdir(gpghome):
        makedirs(gpghome)
        if os.geteuid() == 0 and not snappy.running_in_snap():
            # Make the maas user the owner of its GPG home.  Do this only if
            # running as root; otherwise it would probably fail.  We want to
            # be able to start a development instance without triggering that.
            check_call(["chown", "maas:maas", gpghome])
Beispiel #9
0
def get_config_v4(template_name: str, global_dhcp_snippets: Sequence[dict],
                  failover_peers: Sequence[dict],
                  shared_networks: Sequence[dict], hosts: Sequence[dict],
                  omapi_key: str) -> str:
    """Return a DHCP config file based on the supplied parameters.

    :param template_name: Template file name: `dhcpd.conf.template` for the
        IPv4 template.
    :return: A full configuration, as a string.
    """
    platform_codename = linux_distribution()[2]
    template = load_template('dhcp', template_name)
    dhcp_socket = get_data_path('/var/lib/maas/dhcpd.sock')

    # Helper functions to stuff into the template namespace.
    helpers = {
        "oneline": normalise_whitespace,
        "commalist": normalise_any_iterable_to_comma_list,
        "quoted_commalist": normalise_any_iterable_to_quoted_comma_list,
        "running_in_snap": snappy.running_in_snap(),
    }

    rack_addrs = [
        IPAddress(addr) for addr in net_utils.get_all_interface_addresses()
    ]

    for shared_network in shared_networks:
        for subnet in shared_network["subnets"]:
            cidr = IPNetwork(subnet['subnet_cidr'])
            rack_ips = [
                str(rack_addr) for rack_addr in rack_addrs if rack_addr in cidr
            ]
            if len(rack_ips) > 0:
                subnet["next_server"] = rack_ips[0]
                subnet["bootloader"] = compose_conditional_bootloader(
                    False, rack_ips[0])
            ntp_servers = subnet["ntp_servers"]  # Is a list.
            ntp_servers_ipv4, ntp_servers_ipv6 = _get_addresses(*ntp_servers)
            subnet["ntp_servers_ipv4"] = ", ".join(ntp_servers_ipv4)
            subnet["ntp_servers_ipv6"] = ", ".join(ntp_servers_ipv6)

    try:
        return template.substitute(
            global_dhcp_snippets=global_dhcp_snippets,
            hosts=hosts,
            failover_peers=failover_peers,
            shared_networks=shared_networks,
            platform_codename=platform_codename,
            omapi_key=omapi_key,
            dhcp_helper=(get_path('/usr/sbin/maas-dhcp-helper')),
            dhcp_socket=dhcp_socket,
            **helpers)
    except (KeyError, NameError) as error:
        raise DHCPConfigError(
            "Failed to render DHCP configuration.") from error
Beispiel #10
0
def get_deprecations():
    """Return a list of currently active deprecation notices."""
    deprecations = []

    def add_deprecation(id):
        deprecation = deepcopy(DEPRECATIONS[id])
        deprecation["id"] = id
        deprecation["url"] = DEPRECATION_URL.format(id=id)
        deprecations.append(deprecation)

    if snappy.running_in_snap() and snappy.get_snap_mode() == "all":
        add_deprecation("MD1")

    return deprecations
Beispiel #11
0
def proxy_update_config(reload_proxy=True):
    """Regenerate the proxy configuration file."""

    @transactional
    def _write_config():
        allowed_subnets = Subnet.objects.filter(allow_proxy=True)
        cidrs = [subnet.cidr for subnet in allowed_subnets]
        config = Config.objects.get_configs(
            [
                "http_proxy",
                "maas_proxy_port",
                "use_peer_proxy",
                "prefer_v4_proxy",
                "enable_http_proxy",
            ]
        )

        kwargs = {
            "prefer_v4_proxy": config["prefer_v4_proxy"],
            "maas_proxy_port": config["maas_proxy_port"],
        }
        if (
            config["enable_http_proxy"]
            and config["http_proxy"]
            and config["use_peer_proxy"]
        ):
            kwargs["peer_proxies"] = [config["http_proxy"]]
        write_config(cidrs, **kwargs)

    if is_proxy_enabled():
        d = deferToDatabase(_write_config)
        if reload_proxy:
            # XXX: andreserl 2016-05-09 bug=1687620. When running in a snap,
            # supervisord tracks services. It does not support reloading.
            # Instead, we need to restart the service.
            if snappy.running_in_snap():
                d.addCallback(
                    lambda _: service_monitor.restartService(
                        "proxy", if_on=True
                    )
                )
            else:
                d.addCallback(
                    lambda _: service_monitor.reloadService(
                        "proxy", if_on=True
                    )
                )
        return d
    else:
        return succeed(None)
Beispiel #12
0
    def _applyConfiguration(self, configuration):
        """Configure the HTTP server.

        :param configuration: The configuration object obtained from
            `_getConfiguration`.
        """
        d = deferToThread(self._configure, configuration.upstream_http)
        # XXX: blake_r 2018-06-12 bug=1687620. When running in a snap,
        # supervisord tracks services. It does not support reloading.
        # Instead, we need to restart the service.
        if snappy.running_in_snap():
            d.addCallback(lambda _: service_monitor.restartService("http"))
        else:
            d.addCallback(lambda _: service_monitor.reloadService("http"))
        return d
Beispiel #13
0
 def _performServiceAction(self, service, action):
     """Start or stop the service."""
     lock = self._getServiceLock(service.name)
     if snappy.running_in_snap():
         exec_action = self._execSupervisorServiceAction
         service_name = service.snap_service_name
     else:
         exec_action = self._execSystemDServiceAction
         service_name = service.service_name
     exit_code, output, error = yield lock.run(exec_action, service_name,
                                               action)
     if exit_code != 0:
         error_msg = ("Service '%s' failed to %s: %s" %
                      (service.name, action, error))
         maaslog.error(error_msg)
         raise ServiceActionError(error_msg)
Beispiel #14
0
def get_maas_common_command():
    """Return path to the maas-rack command.

    In production mode this will just return 'maas-rack', but in
    development mode it will return the path for the current development
    environment.
    """
    # Avoid circular imports.
    from provisioningserver.config import is_dev_environment
    if is_dev_environment():
        from maastesting import root
        return os.path.join(root, 'bin/maas-common')
    elif snappy.running_in_snap():
        # there's no maas-common in the snap as maas-rack is always present
        return os.path.join(snappy.get_snap_path(), 'bin/maas-rack')
    else:
        return get_path('usr/lib/maas/maas-common')
Beispiel #15
0
    def write_config():
        allowed_subnets = Subnet.objects.filter(allow_proxy=True)
        cidrs = [subnet.cidr for subnet in allowed_subnets]
        config = Config.objects.get_configs([
            'http_proxy', 'maas_proxy_port', 'use_peer_proxy',
            'prefer_v4_proxy', 'enable_http_proxy'
        ])

        http_proxy = config["http_proxy"]
        upstream_proxy_enabled = (config["use_peer_proxy"] and http_proxy)
        context = {
            'allowed': allowed_subnets,
            'modified': str(datetime.date.today()),
            'fqdn': socket.getfqdn(),
            'cidrs': cidrs,
            'running_in_snap': snappy.running_in_snap(),
            'snap_path': snappy.get_snap_path(),
            'snap_data_path': snappy.get_snap_data_path(),
            'snap_common_path': snappy.get_snap_common_path(),
            'upstream_peer_proxy': upstream_proxy_enabled,
            'dns_v4_first': config['prefer_v4_proxy'],
            'maas_proxy_port': config['maas_proxy_port'],
        }

        proxy_enabled = config["enable_http_proxy"]
        if proxy_enabled and upstream_proxy_enabled:
            http_proxy_hostname = urlparse(http_proxy).hostname
            http_proxy_port = urlparse(http_proxy).port
            context.update({
                'upstream_proxy_address': http_proxy_hostname,
                'upstream_proxy_port': http_proxy_port,
            })

        template_path = locate_template('proxy', MAAS_PROXY_CONF_TEMPLATE)
        template = tempita.Template.from_filename(template_path,
                                                  encoding="UTF-8")
        try:
            content = template.substitute(context)
        except NameError as error:
            raise ProxyConfigFail(*error.args)
        # Squid prefers ascii.
        content = content.encode("ascii")
        target_path = get_proxy_config_path()
        atomic_write(content, target_path, overwrite=True, mode=0o644)
Beispiel #16
0
def sudo_delete_file(filename):
    """Delete file as root.  USE WITH EXTREME CARE.

    Runs an atomic update using non-interactive `sudo`.  This will fail if
    it needs to prompt for a password.

    When running in a snap this function calls `atomic_write` directly.
    """
    from provisioningserver.config import is_dev_environment
    if snappy.running_in_snap():
        atomic_delete(filename)
    else:
        maas_delete_file = get_library_script_path("maas-delete-file")
        command = _with_dev_python(maas_delete_file, filename)
        if not is_dev_environment():
            command = sudo(command)
        proc = Popen(command)
        stdout, stderr = proc.communicate()
        if proc.returncode != 0:
            raise ExternalProcessError(proc.returncode, command, stderr)
Beispiel #17
0
def write_config(
    allowed_cidrs,
    peer_proxies=None,
    prefer_v4_proxy=False,
    maas_proxy_port=8000,
):
    """Write the proxy configuration."""
    if peer_proxies is None:
        peer_proxies = []

    context = {
        "modified": str(datetime.date.today()),
        "fqdn": socket.getfqdn(),
        "cidrs": allowed_cidrs,
        "running_in_snap": snappy.running_in_snap(),
        "snap_path": snappy.get_snap_path(),
        "snap_data_path": snappy.get_snap_data_path(),
        "snap_common_path": snappy.get_snap_common_path(),
        "dns_v4_first": prefer_v4_proxy,
        "maas_proxy_port": maas_proxy_port,
    }

    formatted_peers = []
    for peer in peer_proxies:
        formatted_peers.append({
            "address": urlparse(peer).hostname,
            "port": urlparse(peer).port
        })
    context["peers"] = formatted_peers

    template_path = locate_template("proxy", MAAS_PROXY_CONF_TEMPLATE)
    template = tempita.Template.from_filename(template_path, encoding="UTF-8")
    try:
        content = template.substitute(context)
    except NameError as error:
        raise ProxyConfigFail(*error.args)

    # Squid prefers ascii.
    content = content.encode("ascii")
    target_path = get_proxy_config_path()
    atomic_write(content, target_path, overwrite=True, mode=0o644)
Beispiel #18
0
def get_running_version() -> MAASVersion:
    """Return the version for the running MAAS."""
    git_rev = None
    revno = 0
    if snappy.running_in_snap():
        version_str = snappy.get_snap_version()
    else:
        version_str = _get_version_from_apt(RACK_PACKAGE_NAME,
                                            REGION_PACKAGE_NAME)
    if not version_str:
        version_str = _get_version_from_python_package()
        git_rev = _get_maas_repo_hash()
        revno = _get_maas_repo_commit_count()

    maas_version = MAASVersion.from_string(version_str)
    if (not maas_version.git_rev) and git_rev:
        maas_version = dataclasses.replace(maas_version, git_rev=git_rev)
    if (not maas_version.revno) and revno:
        maas_version = dataclasses.replace(maas_version, revno=revno)

    return maas_version
Beispiel #19
0
def write_config(write_local, forwarders=None, port=None):
    """Write the syslog configuration."""
    context = {
        'user':
        '******',
        'group':
        'maas',
        'drop_priv':
        True,
        'work_dir':
        get_syslog_workdir_path(),
        'log_dir':
        get_syslog_log_path(),
        'write_local':
        write_local,
        'port':
        port if port else 5247,
        'forwarders': (sorted(forwarders, key=itemgetter('name'))
                       if forwarders is not None else []),
    }

    # Running inside the snap rsyslog is root.
    if snappy.running_in_snap():
        context['user'] = '******'
        context['group'] = 'root'
        context['drop_priv'] = False

    template_path = locate_template('syslog', MAAS_SYSLOG_CONF_TEMPLATE)
    template = tempita.Template.from_filename(template_path, encoding="UTF-8")
    try:
        content = template.substitute(context)
    except NameError as error:
        raise SyslogConfigFail(*error.args)

    # Squid prefers ascii.
    content = content.encode("ascii")
    target_path = get_syslog_config_path()
    atomic_write(content, target_path, overwrite=True, mode=0o644)
Beispiel #20
0
def write_config(write_local, forwarders=None, port=None):
    """Write the syslog configuration."""
    context = {
        "user":
        "******",
        "group":
        "maas",
        "drop_priv":
        True,
        "work_dir":
        get_syslog_workdir_path(),
        "log_dir":
        get_syslog_log_path(),
        "write_local":
        write_local,
        "port":
        port if port else 5247,
        "forwarders": (sorted(forwarders, key=itemgetter("name"))
                       if forwarders is not None else []),
    }

    # Running inside the snap rsyslog is root.
    if snappy.running_in_snap():
        context["user"] = "******"
        context["group"] = "root"
        context["drop_priv"] = False

    template_path = locate_template("syslog", MAAS_SYSLOG_CONF_TEMPLATE)
    template = tempita.Template.from_filename(template_path, encoding="UTF-8")
    try:
        content = template.substitute(context)
    except NameError as error:
        raise SyslogConfigFail(*error.args)

    # Squid prefers ascii.
    content = content.encode("ascii")
    target_path = get_syslog_config_path()
    atomic_write(content, target_path, overwrite=True, mode=0o644)
Beispiel #21
0
    def _configure(self, upstream_http):
        """Update the HTTP configuration for the rack."""
        template = load_template("http", "rackd.nginx.conf.template")
        try:
            rendered = template.substitute({
                "upstream_http":
                list(sorted(upstream_http)),
                "resource_root":
                self._resource_root,
                "machine_resources":
                os.path.join(snappy.get_snap_path(), "usr/share/maas") if
                (snappy.running_in_snap()) else "/usr/share/maas",
            })
        except NameError as error:
            raise HTTPConfigFail(*error.args)
        else:
            # The rendered configuration is Unicode text but should contain
            # only ASCII characters.
            rendered = rendered.encode("ascii")

        target_path = compose_http_config_path("rackd.nginx.conf")
        os.makedirs(os.path.dirname(target_path), exist_ok=True)
        atomic_write(rendered, target_path, overwrite=True, mode=0o644)
Beispiel #22
0
def get_maas_version():
    """Return the apt or snap version for the main MAAS package."""
    if snappy.running_in_snap():
        return snappy.get_snap_version()
    else:
        return get_version_from_apt(RACK_PACKAGE_NAME, REGION_PACKAGE_NAME)
Beispiel #23
0
from collections import namedtuple
from functools import lru_cache
import re

from provisioningserver.logger import get_maas_logger
from provisioningserver.utils import (
    shell,
    snappy,
)

maaslog = get_maas_logger('version')

DEFAULT_VERSION = "2.5.0"

# Only import apt_pkg and initialize when not running in a snap.
if not snappy.running_in_snap():
    import apt_pkg
    apt_pkg.init()

# Name of maas package to get version from.
REGION_PACKAGE_NAME = "maas-region-api"
RACK_PACKAGE_NAME = "maas-rack-controller"


def get_version_from_apt(*packages):
    """Return the version output from `apt_pkg.Cache` for the given package(s),
     or log an error message if the package data is not valid."""
    try:
        cache = apt_pkg.Cache(None)
    except SystemError:
        maaslog.error('Installed version could not be determined. Ensure '
Beispiel #24
0
from datetime import datetime
import os
from socket import gethostname
from threading import Lock
from time import sleep

from OpenSSL import crypto

from provisioningserver.path import get_tentative_data_path
from provisioningserver.utils.fs import NamedLock
from provisioningserver.utils.snappy import (
    get_snap_common_path,
    running_in_snap,
)

if running_in_snap():
    MAAS_PRIVATE_KEY = os.path.join(get_snap_common_path(), "certificates",
                                    "maas.key")
    MAAS_PUBLIC_KEY = os.path.join(get_snap_common_path(), "certificates",
                                   "maas.pub")
    MAAS_CERTIFICATE = os.path.join(get_snap_common_path(), "certificates",
                                    "maas.crt")
else:
    MAAS_PRIVATE_KEY = get_tentative_data_path(
        "/etc/maas/certificates/maas.key")
    MAAS_PUBLIC_KEY = get_tentative_data_path(
        "/etc/maas/certificates/maas.pub")
    MAAS_CERTIFICATE = get_tentative_data_path(
        "/etc/maas/certificates/maas.crt")

Beispiel #25
0
def get_deprecations():
    """Return a list of currently active deprecation notices."""
    deprecations = []
    if snappy.running_in_snap() and snappy.get_snap_mode() == "all":
        deprecations.append(DEPRECATIONS["NO_ALL_MODE"])
    return deprecations
Beispiel #26
0
def runscripts(scripts, url, creds, tmpdir):
    total_scripts = len(scripts)
    current_script = 1
    failed_scripts = []
    out_dir = os.path.join(tmpdir, "out")
    os.makedirs(out_dir)
    for script_name in sorted(scripts.keys()):
        builtin_script = scripts[script_name]
        signal_wrapper(
            url,
            creds,
            "WORKING",
            "Starting %s [%d/%d]" %
            (script_name, current_script, total_scripts),
        )

        if script_name == LXD_OUTPUT_NAME:
            # Execute the LXD binary directly as we are already on the
            # rack controller and don't need to download it.
            if running_in_snap():
                script_path = os.path.join(
                    get_snap_path(),
                    "usr/share/maas/machine-resources",
                    get_architecture().split("/")[0],
                )
            else:
                script_path = os.path.join(
                    "/usr/share/maas/machine-resources",
                    get_architecture().split("/")[0],
                )
        else:
            # Write script to /tmp and set it executable
            script_path = os.path.join(tmpdir, script_name)
            with open(script_path, "wb") as f:
                f.write(builtin_script["content"])
            st = os.stat(script_path)
            os.chmod(script_path, st.st_mode | stat.S_IEXEC)

        combined_path = os.path.join(out_dir, script_name)
        stdout_name = "%s.out" % script_name
        stdout_path = os.path.join(out_dir, stdout_name)
        stderr_name = "%s.err" % script_name
        stderr_path = os.path.join(out_dir, stderr_name)
        result_name = "%s.yaml" % script_name
        result_path = os.path.join(out_dir, result_name)

        env = copy.deepcopy(os.environ)
        env["OUTPUT_COMBINED_PATH"] = combined_path
        env["OUTPUT_STDOUT_PATH"] = stdout_path
        env["OUTPUT_STDERR_PATH"] = stderr_path
        env["RESULT_PATH"] = result_path

        timeout = builtin_script.get("timeout")
        if timeout is None:
            timeout_seconds = None
        else:
            timeout_seconds = timeout.seconds

        try:
            proc = Popen(script_path,
                         stdin=DEVNULL,
                         stdout=PIPE,
                         stderr=PIPE,
                         env=env)
            capture_script_output(proc, combined_path, stdout_path,
                                  stderr_path, timeout_seconds)
        except OSError as e:
            if isinstance(e.errno, int) and e.errno != 0:
                exit_status = e.errno
            else:
                # 2 is the return code bash gives when it can't execute.
                exit_status = 2
            result = str(e).encode()
            if result == b"":
                result = b"Unable to execute script"
            files = {script_name: result, stderr_name: result}
            signal_wrapper(
                url,
                creds,
                "WORKING",
                files=files,
                exit_status=exit_status,
                error="Failed to execute %s [%d/%d]: %d" %
                (script_name, current_script, total_scripts, exit_status),
            )
            failed_scripts.append(script_name)
        except TimeoutExpired:
            files = {
                script_name: open(combined_path, "rb").read(),
                stdout_name: open(stdout_path, "rb").read(),
                stderr_name: open(stderr_path, "rb").read(),
            }
            signal_wrapper(
                url,
                creds,
                "TIMEDOUT",
                files=files,
                error="Timeout(%s) expired on %s [%d/%d]" %
                (str(timeout), script_name, current_script, total_scripts),
            )
            failed_scripts.append(script_name)
        else:
            files = {
                script_name: open(combined_path, "rb").read(),
                stdout_name: open(stdout_path, "rb").read(),
                stderr_name: open(stderr_path, "rb").read(),
            }
            if os.path.exists(result_path):
                files[result_name] = open(result_path, "rb").read()
            signal_wrapper(
                url,
                creds,
                "WORKING",
                files=files,
                exit_status=proc.returncode,
                error="Finished %s [%d/%d]: %d" % (
                    script_name,
                    current_script,
                    total_scripts,
                    proc.returncode,
                ),
            )
            if proc.returncode != 0:
                failed_scripts.append(script_name)

        current_script += 1

    return failed_scripts
Beispiel #27
0
def sudo(command_args):
    """Wrap the command arguments in a sudo command, if not in debug mode."""
    if snappy.running_in_snap():
        return command_args
    else:
        return ['sudo', '-n', *command_args]
Beispiel #28
0
 def test_running_in_snap_returns_False(self):
     self.patch(os, "environ", {})
     self.assertFalse(snappy.running_in_snap())
Beispiel #29
0
 def _loadServiceState(self, service):
     """Return service status."""
     if snappy.running_in_snap():
         return self._loadSupervisorServiceState(service)
     else:
         return self._loadSystemDServiceState(service)
Beispiel #30
0
 def test_running_in_snap_returns_True(self):
     self.patch(os, "environ", {"SNAP": factory.make_name()})
     self.assertTrue(snappy.running_in_snap())