Пример #1
0
def inhibit_default_cluster_creation():
    """Stop the PostgreSQL packages from creating the default cluster.

    We can't use the default cluster as it is likely created with an
    incorrect locale and without options such as data checksumming.
    Allowing the package to create the cluster is problematic, as the
    charm can't really tell between a cluster created by package
    installation that can be safely destroyed, and a cluster left
    from a previous installation that might contain precious data that
    we can't risk destroying.
    """
    if host.get_distrib_codename() == "xenial":
        # Xenial's postgresql-common package does not support includes in
        # cluster configuration files.
        os.makedirs("/etc/postgresql-common", mode=0o755, exist_ok=True)
        content = "\n".join(
            [
                "ssl = on",
                "stats_temp_directory = '/var/run/postgresql/%v-%c.pg_stat_tmp'",
                "log_line_prefix = '%%t [%%p-%%l] %%q%%u@%%d '",
                "create_main_cluster = false",
            ]
        )
        host.write_file("/etc/postgresql-common/createcluster.conf", content, perms=0o444)
        return

    path = createcluster_conf_path()
    if os.path.exists(path):
        return
    os.makedirs(os.path.dirname(path), mode=0o755, exist_ok=True)
    host.write_file(path, "create_main_cluster = false", perms=0o444)
Пример #2
0
def __add_bare_helper(openstack_release, pocket_format, final_function):
    """Helper for _add_bare_openstack[_proposed]

    The bulk of the work between the two functions is exactly the same except
    for the pocket format and the function that is run if it's the distro
    version.

    :param openstack_release: the OpenStack codename.  e.g. ussuri
    :type openstack_release: str
    :param pocket_format: the pocket formatter string to construct a pocket str
        from the openstack_release and the current ubuntu version.
    :type pocket_format: str
    :param final_function: the function to call if it is the distro version.
    :type final_function: Callable
    :raises SourceConfigError on error
    """
    ubuntu_version = get_distrib_codename()
    possible_pocket = pocket_format.format(ubuntu_version, openstack_release)
    if possible_pocket in CLOUD_ARCHIVE_POCKETS:
        _add_cloud_pocket(possible_pocket)
        return
    # Otherwise it's almost certainly the distro version; verify that it
    # exists.
    try:
        assert UBUNTU_OPENSTACK_RELEASE[ubuntu_version] == openstack_release
    except KeyError:
        raise SourceConfigError(
            "Invalid ubuntu version {} isn't known to this library".format(
                ubuntu_version))
    except AssertionError:
        raise SourceConfigError(
            'Invalid OpenStack release specified: {} for Ubuntu version {}'.
            format(openstack_release, ubuntu_version))
    final_function()
Пример #3
0
def _get_keyid_by_gpg_key(key_material):
    """Get a GPG key fingerprint by GPG key material.
    Gets a GPG key fingerprint (40-digit, 160-bit) by the ASCII armor-encoded
    or binary GPG key material. Can be used, for example, to generate file
    names for keys passed via charm options.

    :param key_material: ASCII armor-encoded or binary GPG key material
    :type key_material: bytes
    :raises: GPGKeyError if invalid key material has been provided
    :returns: A GPG key fingerprint
    :rtype: str
    """
    # trusty, xenial and bionic handling differs due to gpg 1.x to 2.x change
    release = get_distrib_codename()
    is_gpgv2_distro = CompareHostReleases(release) >= "bionic"
    if is_gpgv2_distro:
        # --import is mandatory, otherwise fingerprint is not printed
        cmd = 'gpg --with-colons --import-options show-only --import --dry-run'
    else:
        cmd = 'gpg --with-colons --with-fingerprint'
    ps = subprocess.Popen(cmd.split(),
                          stdout=subprocess.PIPE,
                          stderr=subprocess.PIPE,
                          stdin=subprocess.PIPE)
    out, err = ps.communicate(input=key_material)
    if six.PY3:
        out = out.decode('utf-8')
        err = err.decode('utf-8')
    if 'gpg: no valid OpenPGP data found.' in err:
        raise GPGKeyError('Invalid GPG key material provided')
    # from gnupg2 docs: fpr :: Fingerprint (fingerprint is in field 10)
    return re.search(r"^fpr:{9}([0-9A-F]{40}):$", out, re.MULTILINE).group(1)
Пример #4
0
def _add_apt_repository(spec):
    """Add the spec using add_apt_repository

    :param spec: the parameter to pass to add_apt_repository
    :type spec: str
    """
    if '{series}' in spec:
        series = get_distrib_codename()
        spec = spec.replace('{series}', series)
    _run_with_retries(['add-apt-repository', '--yes', spec],
                      cmd_env=env_proxy_settings(['https', 'http']))
Пример #5
0
 def add_sources(self):
     """Ensure the GitLab apt repository is configured and updated for use."""
     distro = host.get_distrib_codename()
     apt_repo = self.charm_config.get("apt_repo")
     apt_key = self.charm_config.get("apt_key")
     apt_line = "deb {}/{}/ubuntu {} main".format(apt_repo,
                                                  self.package_name, distro)
     hookenv.log(
         "Installing and updating apt source for {}: {} key {})".format(
             self.package_name, apt_line, apt_key))
     add_source(apt_line, apt_key)
Пример #6
0
 def __init__(self):
     """Load hookenv key/value store and charm configuration."""
     self.charm_config = hookenv.config()
     if self.charm_config["version"]:
         self.version = self.charm_config["version"]
     else:
         self.version = None
     self.set_package_name(self.charm_config["package_name"])
     self.kv = unitdata.kv()
     self.gitlab_commands_file = "/etc/gitlab/commands.load"
     self.distro = host.get_distrib_codename()
Пример #7
0
 def add_sources(self):
     """Add APT sources to allow installation of GitLab Runner from GitLab's packages."""
     # https://packages.gitlab.com/runner/gitlab-runner/gpgkey
     # https://packages.gitlab.com/runner/gitlab-runner/ubuntu/ bionic main
     distro = get_distrib_codename()
     apt_key = "14219A96E15E78F4"
     apt_line = "deb https://packages.gitlab.com/runner/gitlab-runner/ubuntu/ {} main".format(
         distro)
     hookenv.log(
         "Installing and updating apt source for gitlab-runner: {} key {})".
         format(apt_line, apt_key))
     add_source(apt_line, apt_key)
     return True
Пример #8
0
def _verify_is_ubuntu_rel(release, os_release):
    """Verify that the release is in the same as the current ubuntu release.

    :param release: String, lowercase for the release.
    :param os_release: String, the os_release being asked for
    :raises: SourceConfigError if the release is not the same as the ubuntu
        release.
    """
    ubuntu_rel = get_distrib_codename()
    if release != ubuntu_rel:
        raise SourceConfigError(
            'Invalid Cloud Archive release specified: {}-{} on this Ubuntu'
            'version ({})'.format(release, os_release, ubuntu_rel))
Пример #9
0
def _verify_is_ubuntu_rel(release, os_release):
    """Verify that the release is in the same as the current ubuntu release.

    :param release: String, lowercase for the release.
    :param os_release: String, the os_release being asked for
    :raises: SourceConfigError if the release is not the same as the ubuntu
        release.
    """
    ubuntu_rel = get_distrib_codename()
    if release != ubuntu_rel:
        raise SourceConfigError(
            'Invalid Cloud Archive release specified: {}-{} on this Ubuntu'
            'version ({})'.format(release, os_release, ubuntu_rel))
Пример #10
0
def _add_apt_repository(spec):
    """Add the spec using add_apt_repository

    :param spec: the parameter to pass to add_apt_repository
    :type spec: str
    """
    if '{series}' in spec:
        series = get_distrib_codename()
        spec = spec.replace('{series}', series)
    # software-properties package for bionic properly reacts to proxy settings
    # passed as environment variables (See lp:1433761). This is not the case
    # LTS and non-LTS releases below bionic.
    _run_with_retries(['add-apt-repository', '--yes', spec],
                      cmd_env=env_proxy_settings(['https']))
Пример #11
0
def _add_apt_repository(spec):
    """Add the spec using add_apt_repository

    :param spec: the parameter to pass to add_apt_repository
    :type spec: str
    """
    if '{series}' in spec:
        series = get_distrib_codename()
        spec = spec.replace('{series}', series)
    # software-properties package for bionic properly reacts to proxy settings
    # passed as environment variables (See lp:1433761). This is not the case
    # LTS and non-LTS releases below bionic.
    _run_with_retries(['add-apt-repository', '--yes', spec],
                      cmd_env=env_proxy_settings(['https']))
Пример #12
0
    def check_version(self):
        """Chcek version for upgrade support."""
        if host.get_distrib_codename() == "xenial":
            supported_versions = ("1.7", )

            if self.charm_config["version"] not in supported_versions:
                msg = "Version {} must be in {} for xenial".format(
                    self.charm_config["version"], supported_versions)

                return (False, msg, supported_versions[0])
            else:
                return (True, "Version supported", None)
        elif host.get_distrib_codename() == "bionic":
            supported_versions = ("1.8", "1.9")

            if self.charm_config["version"] not in supported_versions:
                msg = "Version {} must be in {} for bionic".format(
                    self.charm_config["version"], supported_versions)

                return (False, msg, supported_versions[0])
            else:
                return (True, "Version supported", None)

        return (False, "Version check failed")
Пример #13
0
def _add_proposed():
    """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list

    Uses get_distrib_codename to determine the correct stanza for
    the deb line.

    For Intel architectures PROPOSED_POCKET is used for the release, but for
    other architectures PROPOSED_PORTS_POCKET is used for the release.
    """
    release = get_distrib_codename()
    arch = platform.machine()
    if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET):
        raise SourceConfigError(
            "Arch {} not supported for (distro-)proposed".format(arch))
    with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
        apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
Пример #14
0
def _add_proposed():
    """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list

    Uses get_distrib_codename to determine the correct stanza for
    the deb line.

    For intel architecutres PROPOSED_POCKET is used for the release, but for
    other architectures PROPOSED_PORTS_POCKET is used for the release.
    """
    release = get_distrib_codename()
    arch = platform.machine()
    if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET):
        raise SourceConfigError("Arch {} not supported for (distro-)proposed"
                                .format(arch))
    with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
        apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
Пример #15
0
def _add_apt_repository(spec):
    """Add the spec using add_apt_repository

    :param spec: the parameter to pass to add_apt_repository
    :type spec: str
    """
    series = get_distrib_codename()
    if '{series}' in spec:
        spec = spec.replace('{series}', series)
    # software-properties package for bionic properly reacts to proxy settings
    # set via apt.conf (see lp:1433761), however this is not the case for LTS
    # and non-LTS releases before bionic.
    if series in ('trusty', 'xenial'):
        _run_with_retries(['add-apt-repository', '--yes', spec],
                          cmd_env=env_proxy_settings(['https', 'http']))
    else:
        _run_with_retries(['add-apt-repository', '--yes', spec])
Пример #16
0
def update_nrpe_config():
    # Validate options (DEPRECATED)
    valid_alerts = ['ignore', 'warning', 'critical']
    if config('failed_actions_alert_type').lower() not in valid_alerts:
        status_set(
            'blocked', 'The value of option failed_actions_alert_type must be '
            'among {}'.format(valid_alerts))
        return
    if config('failed_actions_threshold') < 0:
        status_set(
            'blocked',
            'The value of option failed_actions_threshold must be a '
            'positive integer')
        return

    scripts_src = os.path.join(os.environ["CHARM_DIR"], "files", "nrpe")

    scripts_dst = "/usr/local/lib/nagios/plugins"
    if not os.path.exists(scripts_dst):
        os.makedirs(scripts_dst)
    for fname in glob.glob(os.path.join(scripts_src, "*")):
        if os.path.isfile(fname):
            shutil.copy2(fname,
                         os.path.join(scripts_dst, os.path.basename(fname)))

    sudoers_src = os.path.join(os.environ["CHARM_DIR"], "files", "sudoers")
    sudoers_dst = "/etc/sudoers.d"
    for fname in glob.glob(os.path.join(sudoers_src, "*")):
        if os.path.isfile(fname):
            shutil.copy2(fname,
                         os.path.join(sudoers_dst, os.path.basename(fname)))

    hostname = nrpe.get_nagios_hostname()
    current_unit = nrpe.get_nagios_unit_name()

    nrpe_setup = nrpe.NRPE(hostname=hostname)

    apt_install('python-dbus')

    check_crm_cmd = 'check_crm -s'
    check_crm_cmd += ' --failedactions={}'.format(
        config('failed_actions_alert_type').lower())
    if config('failed_actions_threshold'):
        check_crm_cmd += ' --failcount={}'.format(
            config('failed_actions_threshold'))
    for err_type in ['warn', 'crit']:
        check_crm_cmd += ' --failcount-{}={}'.format(
            err_type,
            config('res_failcount_{}'.format(err_type)) or 0)

    if nrpe.NRPE.does_nrpe_conf_dir_exist():
        # corosync/crm checks

        # LP #1902919 - corosync version 2.99 changed the ring status output
        # for udp/udpu to hardcode the status to always report 'OK'. This
        # results in the check providing no value over what is provided by the
        # crm_status check. A version check on the package would be more ideal,
        # however populating the apt-cache object is expensive to run on each
        # config-changed hook, so use the faster check of comparing the
        # release name.
        ring_check = {
            'shortname': 'corosync_rings',
            'description': 'Check Corosync rings {}'.format(current_unit),
            'check_cmd': 'check_corosync_rings',
        }
        if CompareHostReleases(get_distrib_codename()) < 'eoan':
            nrpe_setup.add_check(**ring_check)
        else:
            nrpe_setup.remove_check(**ring_check)

        nrpe_setup.add_check(
            shortname='crm_status',
            description='Check crm status {}'.format(current_unit),
            check_cmd=check_crm_cmd)

        # process checks
        nrpe_setup.add_check(
            shortname='corosync_proc',
            description='Check Corosync process {}'.format(current_unit),
            check_cmd='check_procs -c 1:1 -C corosync')
        nrpe_setup.add_check(
            shortname='pacemakerd_proc',
            description='Check Pacemakerd process {}'.format(current_unit),
            check_cmd='check_procs -c 1:1 -C pacemakerd')

        nrpe_setup.write()