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)
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()
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)
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']))
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)
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()
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
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))
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']))
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")
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))
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))
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])
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()