Blame cloudinit/config/cc_puppet.py

Packit Service a04d08
# Copyright (C) 2009-2010 Canonical Ltd.
Packit Service a04d08
# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
Packit Service a04d08
#
Packit Service a04d08
# Author: Scott Moser <scott.moser@canonical.com>
Packit Service a04d08
# Author: Juerg Haefliger <juerg.haefliger@hp.com>
Packit Service a04d08
#
Packit Service a04d08
# This file is part of cloud-init. See LICENSE file for license information.
Packit Service a04d08
Packit Service a04d08
"""
Packit Service a04d08
Puppet
Packit Service a04d08
------
Packit Service a04d08
**Summary:** install, configure and start puppet
Packit Service a04d08
Packit Service a04d08
This module handles puppet installation and configuration. If the ``puppet``
Packit Service a04d08
key does not exist in global configuration, no action will be taken. If a
Packit Service a04d08
config entry for ``puppet`` is present, then by default the latest version of
Packit Service a04d08
puppet will be installed. If ``install`` is set to ``false``, puppet will not
Packit Service a04d08
be installed. However, this will result in an error if puppet is not already
Packit Service a04d08
present on the system. The version of puppet to be installed can be specified
Packit Service a04d08
under ``version``, and defaults to ``none``, which selects the latest version
Packit Service a04d08
in the repos. If the ``puppet`` config key exists in the config archive, this
Packit Service a04d08
module will attempt to start puppet even if no installation was performed.
Packit Service a04d08
Packit Service a04d08
The module also provides keys for configuring the new puppet 4 paths and
Packit Service a04d08
installing the puppet package from the puppetlabs repositories:
Packit Service a04d08
https://docs.puppet.com/puppet/4.2/reference/whered_it_go.html
Packit Service a04d08
The keys are ``package_name``, ``conf_file``, ``ssl_dir`` and
Packit Service a04d08
``csr_attributes_path``. If unset, their values will default to
Packit Service a04d08
ones that work with puppet 3.x and with distributions that ship modified
Packit Service a04d08
puppet 4.x that uses the old paths.
Packit Service a04d08
Packit Service a04d08
Puppet configuration can be specified under the ``conf`` key. The
Packit Service a04d08
configuration is specified as a dictionary containing high-level ``<section>``
Packit Service a04d08
keys and lists of ``<key>=<value>`` pairs within each section. Each section
Packit Service a04d08
name and ``<key>=<value>`` pair is written directly to ``puppet.conf``. As
Packit Service a04d08
such,  section names should be one of: ``main``, ``master``, ``agent`` or
Packit Service a04d08
``user`` and keys should be valid puppet configuration options. The
Packit Service a04d08
``certname`` key supports string substitutions for ``%i`` and ``%f``,
Packit Service a04d08
corresponding to the instance id and fqdn of the machine respectively.
Packit Service a04d08
If ``ca_cert`` is present, it will not be written to ``puppet.conf``, but
Packit Service a04d08
instead will be used as the puppermaster certificate. It should be specified
Packit Service a04d08
in pem format as a multi-line string (using the ``|`` yaml notation).
Packit Service a04d08
Packit Service a04d08
Additionally it's possible to create a csr_attributes.yaml for
Packit Service a04d08
CSR attributes and certificate extension requests.
Packit Service a04d08
See https://puppet.com/docs/puppet/latest/config_file_csr_attributes.html
Packit Service a04d08
Packit Service a04d08
**Internal name:** ``cc_puppet``
Packit Service a04d08
Packit Service a04d08
**Module frequency:** per instance
Packit Service a04d08
Packit Service a04d08
**Supported distros:** all
Packit Service a04d08
Packit Service a04d08
**Config keys**::
Packit Service a04d08
Packit Service a04d08
    puppet:
Packit Service a04d08
        install: <true/false>
Packit Service a04d08
        version: <version>
Packit Service a04d08
        conf_file: '/etc/puppet/puppet.conf'
Packit Service a04d08
        ssl_dir: '/var/lib/puppet/ssl'
Packit Service a04d08
        csr_attributes_path: '/etc/puppet/csr_attributes.yaml'
Packit Service a04d08
        package_name: 'puppet'
Packit Service a04d08
        conf:
Packit Service a04d08
            agent:
Packit Service a04d08
                server: "puppetmaster.example.org"
Packit Service a04d08
                certname: "%i.%f"
Packit Service a04d08
                ca_cert: |
Packit Service a04d08
                    -------BEGIN CERTIFICATE-------
Packit Service a04d08
                    <cert data>
Packit Service a04d08
                    -------END CERTIFICATE-------
Packit Service a04d08
        csr_attributes:
Packit Service a04d08
            custom_attributes:
Packit Service a04d08
                1.2.840.113549.1.9.7: 342thbjkt82094y0uthhor289jnqthpc2290
Packit Service a04d08
            extension_requests:
Packit Service a04d08
                pp_uuid: ED803750-E3C7-44F5-BB08-41A04433FE2E
Packit Service a04d08
                pp_image_name: my_ami_image
Packit Service a04d08
                pp_preshared_key: 342thbjkt82094y0uthhor289jnqthpc2290
Packit Service a04d08
"""
Packit Service a04d08
Packit Service a04d08
import os
Packit Service a04d08
import socket
Packit Service a04d08
import yaml
Packit Service 751c4a
from io import StringIO
Packit Service a04d08
Packit Service a04d08
from cloudinit import helpers
Packit Service 751c4a
from cloudinit import subp
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
Packit Service a04d08
PUPPET_CONF_PATH = '/etc/puppet/puppet.conf'
Packit Service a04d08
PUPPET_SSL_DIR = '/var/lib/puppet/ssl'
Packit Service a04d08
PUPPET_CSR_ATTRIBUTES_PATH = '/etc/puppet/csr_attributes.yaml'
Packit Service a04d08
PUPPET_PACKAGE_NAME = 'puppet'
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
class PuppetConstants(object):
Packit Service a04d08
Packit Service a04d08
    def __init__(self, puppet_conf_file, puppet_ssl_dir,
Packit Service a04d08
                 csr_attributes_path, log):
Packit Service a04d08
        self.conf_path = puppet_conf_file
Packit Service a04d08
        self.ssl_dir = puppet_ssl_dir
Packit Service a04d08
        self.ssl_cert_dir = os.path.join(puppet_ssl_dir, "certs")
Packit Service a04d08
        self.ssl_cert_path = os.path.join(self.ssl_cert_dir, "ca.pem")
Packit Service a04d08
        self.csr_attributes_path = csr_attributes_path
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _autostart_puppet(log):
Packit Service a04d08
    # Set puppet to automatically start
Packit Service a04d08
    if os.path.exists('/etc/default/puppet'):
Packit Service 751c4a
        subp.subp(['sed', '-i',
Packit Service a04d08
                   '-e', 's/^START=.*/START=yes/',
Packit Service a04d08
                   '/etc/default/puppet'], capture=False)
Packit Service a04d08
    elif os.path.exists('/bin/systemctl'):
Packit Service 751c4a
        subp.subp(['/bin/systemctl', 'enable', 'puppet.service'],
Packit Service a04d08
                  capture=False)
Packit Service a04d08
    elif os.path.exists('/sbin/chkconfig'):
Packit Service 751c4a
        subp.subp(['/sbin/chkconfig', 'puppet', 'on'], capture=False)
Packit Service a04d08
    else:
Packit Service a04d08
        log.warning(("Sorry we do not know how to enable"
Packit Service a04d08
                     " puppet services on this system"))
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle(name, cfg, cloud, log, _args):
Packit Service a04d08
    # If there isn't a puppet key in the configuration don't do anything
Packit Service a04d08
    if 'puppet' not in cfg:
Packit Service a04d08
        log.debug(("Skipping module named %s,"
Packit Service a04d08
                   " no 'puppet' configuration found"), name)
Packit Service a04d08
        return
Packit Service a04d08
Packit Service a04d08
    puppet_cfg = cfg['puppet']
Packit Service a04d08
    # Start by installing the puppet package if necessary...
Packit Service a04d08
    install = util.get_cfg_option_bool(puppet_cfg, 'install', True)
Packit Service a04d08
    version = util.get_cfg_option_str(puppet_cfg, 'version', None)
Packit Service a04d08
    package_name = util.get_cfg_option_str(
Packit Service a04d08
        puppet_cfg, 'package_name', PUPPET_PACKAGE_NAME)
Packit Service a04d08
    conf_file = util.get_cfg_option_str(
Packit Service a04d08
        puppet_cfg, 'conf_file', PUPPET_CONF_PATH)
Packit Service a04d08
    ssl_dir = util.get_cfg_option_str(puppet_cfg, 'ssl_dir', PUPPET_SSL_DIR)
Packit Service a04d08
    csr_attributes_path = util.get_cfg_option_str(
Packit Service a04d08
        puppet_cfg, 'csr_attributes_path', PUPPET_CSR_ATTRIBUTES_PATH)
Packit Service a04d08
Packit Service a04d08
    p_constants = PuppetConstants(conf_file, ssl_dir, csr_attributes_path, log)
Packit Service a04d08
    if not install and version:
Packit Service a04d08
        log.warning(("Puppet install set false but version supplied,"
Packit Service a04d08
                     " doing nothing."))
Packit Service a04d08
    elif install:
Packit Service a04d08
        log.debug(("Attempting to install puppet %s,"),
Packit Service a04d08
                  version if version else 'latest')
Packit Service a04d08
Packit Service a04d08
        cloud.distro.install_packages((package_name, version))
Packit Service a04d08
Packit Service a04d08
    # ... and then update the puppet configuration
Packit Service a04d08
    if 'conf' in puppet_cfg:
Packit Service a04d08
        # Add all sections from the conf object to puppet.conf
Packit Service a04d08
        contents = util.load_file(p_constants.conf_path)
Packit Service a04d08
        # Create object for reading puppet.conf values
Packit Service a04d08
        puppet_config = helpers.DefaultingConfigParser()
Packit Service a04d08
        # Read puppet.conf values from original file in order to be able to
Packit Service a04d08
        # mix the rest up. First clean them up
Packit Service a04d08
        # (TODO(harlowja) is this really needed??)
Packit Service a04d08
        cleaned_lines = [i.lstrip() for i in contents.splitlines()]
Packit Service a04d08
        cleaned_contents = '\n'.join(cleaned_lines)
Packit Service a04d08
        # Move to puppet_config.read_file when dropping py2.7
Packit Service 751c4a
        puppet_config.read_file(
Packit Service a04d08
            StringIO(cleaned_contents),
Packit Service 751c4a
            source=p_constants.conf_path)
Packit Service a04d08
        for (cfg_name, cfg) in puppet_cfg['conf'].items():
Packit Service a04d08
            # Cert configuration is a special case
Packit Service a04d08
            # Dump the puppet master ca certificate in the correct place
Packit Service a04d08
            if cfg_name == 'ca_cert':
Packit Service a04d08
                # Puppet ssl sub-directory isn't created yet
Packit Service a04d08
                # Create it with the proper permissions and ownership
Packit Service a04d08
                util.ensure_dir(p_constants.ssl_dir, 0o771)
Packit Service a04d08
                util.chownbyname(p_constants.ssl_dir, 'puppet', 'root')
Packit Service a04d08
                util.ensure_dir(p_constants.ssl_cert_dir)
Packit Service a04d08
Packit Service a04d08
                util.chownbyname(p_constants.ssl_cert_dir, 'puppet', 'root')
Packit Service a04d08
                util.write_file(p_constants.ssl_cert_path, cfg)
Packit Service a04d08
                util.chownbyname(p_constants.ssl_cert_path, 'puppet', 'root')
Packit Service a04d08
            else:
Packit Service a04d08
                # Iterate through the config items, we'll use ConfigParser.set
Packit Service a04d08
                # to overwrite or create new items as needed
Packit Service a04d08
                for (o, v) in cfg.items():
Packit Service a04d08
                    if o == 'certname':
Packit Service a04d08
                        # Expand %f as the fqdn
Packit Service a04d08
                        # TODO(harlowja) should this use the cloud fqdn??
Packit Service a04d08
                        v = v.replace("%f", socket.getfqdn())
Packit Service a04d08
                        # Expand %i as the instance id
Packit Service a04d08
                        v = v.replace("%i", cloud.get_instance_id())
Packit Service a04d08
                        # certname needs to be downcased
Packit Service a04d08
                        v = v.lower()
Packit Service a04d08
                    puppet_config.set(cfg_name, o, v)
Packit Service a04d08
            # We got all our config as wanted we'll rename
Packit Service a04d08
            # the previous puppet.conf and create our new one
Packit Service a04d08
            util.rename(p_constants.conf_path, "%s.old"
Packit Service a04d08
                        % (p_constants.conf_path))
Packit Service a04d08
            util.write_file(p_constants.conf_path, puppet_config.stringify())
Packit Service a04d08
Packit Service a04d08
    if 'csr_attributes' in puppet_cfg:
Packit Service a04d08
        util.write_file(p_constants.csr_attributes_path,
Packit Service a04d08
                        yaml.dump(puppet_cfg['csr_attributes'],
Packit Service a04d08
                                  default_flow_style=False))
Packit Service a04d08
Packit Service a04d08
    # Set it up so it autostarts
Packit Service a04d08
    _autostart_puppet(log)
Packit Service a04d08
Packit Service a04d08
    # Start puppetd
Packit Service 751c4a
    subp.subp(['service', 'puppet', 'start'], capture=False)
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab