Blame cloudinit/config/cc_zypper_add_repo.py

Packit Service a04d08
#
Packit Service a04d08
#    Copyright (C) 2017 SUSE LLC.
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
"""zypper_add_repo: Add zyper repositories to the system"""
Packit Service a04d08
Packit Service a04d08
import configobj
Packit Service a04d08
import os
Packit Service a04d08
from textwrap import dedent
Packit Service a04d08
Packit Service a04d08
from cloudinit.config.schema import get_schema_doc
Packit Service a04d08
from cloudinit import log as logging
Packit Service a04d08
from cloudinit.settings import PER_ALWAYS
Packit Service a04d08
from cloudinit import util
Packit Service a04d08
Packit Service a04d08
distros = ['opensuse', 'sles']
Packit Service a04d08
Packit Service a04d08
schema = {
Packit Service a04d08
    'id': 'cc_zypper_add_repo',
Packit Service a04d08
    'name': 'ZypperAddRepo',
Packit Service a04d08
    'title': 'Configure zypper behavior and add zypper repositories',
Packit Service a04d08
    'description': dedent("""\
Packit Service a04d08
        Configure zypper behavior by modifying /etc/zypp/zypp.conf. The
Packit Service a04d08
        configuration writer is "dumb" and will simply append the provided
Packit Service a04d08
        configuration options to the configuration file. Option settings
Packit Service a04d08
        that may be duplicate will be resolved by the way the zypp.conf file
Packit Service a04d08
        is parsed. The file is in INI format.
Packit Service a04d08
        Add repositories to the system. No validation is performed on the
Packit Service a04d08
        repository file entries, it is assumed the user is familiar with
Packit Service a04d08
        the zypper repository file format."""),
Packit Service a04d08
    'distros': distros,
Packit Service a04d08
    'examples': [dedent("""\
Packit Service a04d08
        zypper:
Packit Service a04d08
          repos:
Packit Service a04d08
            - id: opensuse-oss
Packit Service a04d08
              name: os-oss
Packit Service a04d08
              baseurl: http://dl.opensuse.org/dist/leap/v/repo/oss/
Packit Service a04d08
              enabled: 1
Packit Service a04d08
              autorefresh: 1
Packit Service a04d08
            - id: opensuse-oss-update
Packit Service a04d08
              name: os-oss-up
Packit Service a04d08
              baseurl: http://dl.opensuse.org/dist/leap/v/update
Packit Service a04d08
              # any setting per
Packit Service a04d08
              # https://en.opensuse.org/openSUSE:Standards_RepoInfo
Packit Service a04d08
              # enable and autorefresh are on by default
Packit Service a04d08
          config:
Packit Service a04d08
            reposdir: /etc/zypp/repos.dir
Packit Service a04d08
            servicesdir: /etc/zypp/services.d
Packit Service a04d08
            download.use_deltarpm: true
Packit Service a04d08
            # any setting in /etc/zypp/zypp.conf
Packit Service a04d08
    """)],
Packit Service a04d08
    'frequency': PER_ALWAYS,
Packit Service a04d08
    'type': 'object',
Packit Service a04d08
    'properties': {
Packit Service a04d08
        'zypper': {
Packit Service a04d08
            'type': 'object',
Packit Service a04d08
            'properties': {
Packit Service a04d08
                'repos': {
Packit Service a04d08
                    'type': 'array',
Packit Service a04d08
                    'items': {
Packit Service a04d08
                        'type': 'object',
Packit Service a04d08
                        'properties': {
Packit Service a04d08
                            'id': {
Packit Service a04d08
                                'type': 'string',
Packit Service a04d08
                                'description': dedent("""\
Packit Service a04d08
                                    The unique id of the repo, used when
Packit Service a04d08
                                     writing
Packit Service a04d08
                                    /etc/zypp/repos.d/<id>.repo.""")
Packit Service a04d08
                            },
Packit Service a04d08
                            'baseurl': {
Packit Service a04d08
                                'type': 'string',
Packit Service a04d08
                                'format': 'uri',   # built-in format type
Packit Service a04d08
                                'description': 'The base repositoy URL'
Packit Service a04d08
                            }
Packit Service a04d08
                        },
Packit Service a04d08
                        'required': ['id', 'baseurl'],
Packit Service a04d08
                        'additionalProperties': True
Packit Service a04d08
                    },
Packit Service a04d08
                    'minItems': 1
Packit Service a04d08
                },
Packit Service a04d08
                'config': {
Packit Service a04d08
                    'type': 'object',
Packit Service a04d08
                    'description': dedent("""\
Packit Service a04d08
                        Any supported zypo.conf key is written to
Packit Service a04d08
                        /etc/zypp/zypp.conf'""")
Packit Service a04d08
                }
Packit Service a04d08
            },
Packit Service a04d08
            'required': [],
Packit Service a04d08
            'minProperties': 1,  # Either config or repo must be provided
Packit Service a04d08
            'additionalProperties': False,  # only repos and config allowed
Packit Service a04d08
        }
Packit Service a04d08
    }
Packit Service a04d08
}
Packit Service a04d08
Packit Service a04d08
__doc__ = get_schema_doc(schema)  # Supplement python help()
Packit Service a04d08
Packit Service a04d08
LOG = logging.getLogger(__name__)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _canonicalize_id(repo_id):
Packit Service a04d08
    repo_id = repo_id.replace(" ", "_")
Packit Service a04d08
    return repo_id
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _format_repo_value(val):
Packit Service a04d08
    if isinstance(val, bool):
Packit Service a04d08
        # zypp prefers 1/0
Packit Service a04d08
        return 1 if val else 0
Packit Service a04d08
    if isinstance(val, (list, tuple)):
Packit Service a04d08
        return "\n    ".join([_format_repo_value(v) for v in val])
Packit Service 751c4a
    if not isinstance(val, str):
Packit Service a04d08
        return str(val)
Packit Service a04d08
    return val
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _format_repository_config(repo_id, repo_config):
Packit Service a04d08
    to_be = configobj.ConfigObj()
Packit Service a04d08
    to_be[repo_id] = {}
Packit Service a04d08
    # Do basic translation of the items -> values
Packit Service a04d08
    for (k, v) in repo_config.items():
Packit Service a04d08
        # For now assume that people using this know the format
Packit Service a04d08
        # of zypper repos  and don't verify keys/values further
Packit Service a04d08
        to_be[repo_id][k] = _format_repo_value(v)
Packit Service a04d08
    lines = to_be.write()
Packit Service a04d08
    return "\n".join(lines)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _write_repos(repos, repo_base_path):
Packit Service a04d08
    """Write the user-provided repo definition files
Packit Service a04d08
    @param repos: A list of repo dictionary objects provided by the user's
Packit Service a04d08
        cloud config.
Packit Service a04d08
    @param repo_base_path: The directory path to which repo definitions are
Packit Service a04d08
        written.
Packit Service a04d08
    """
Packit Service a04d08
Packit Service a04d08
    if not repos:
Packit Service a04d08
        return
Packit Service a04d08
    valid_repos = {}
Packit Service a04d08
    for index, user_repo_config in enumerate(repos):
Packit Service a04d08
        # Skip on absent required keys
Packit Service a04d08
        missing_keys = set(['id', 'baseurl']).difference(set(user_repo_config))
Packit Service a04d08
        if missing_keys:
Packit Service a04d08
            LOG.warning(
Packit Service a04d08
                "Repo config at index %d is missing required config keys: %s",
Packit Service a04d08
                index, ",".join(missing_keys))
Packit Service a04d08
            continue
Packit Service a04d08
        repo_id = user_repo_config.get('id')
Packit Service a04d08
        canon_repo_id = _canonicalize_id(repo_id)
Packit Service a04d08
        repo_fn_pth = os.path.join(repo_base_path, "%s.repo" % (canon_repo_id))
Packit Service a04d08
        if os.path.exists(repo_fn_pth):
Packit Service a04d08
            LOG.info("Skipping repo %s, file %s already exists!",
Packit Service a04d08
                     repo_id, repo_fn_pth)
Packit Service a04d08
            continue
Packit Service a04d08
        elif repo_id in valid_repos:
Packit Service a04d08
            LOG.info("Skipping repo %s, file %s already pending!",
Packit Service a04d08
                     repo_id, repo_fn_pth)
Packit Service a04d08
            continue
Packit Service a04d08
Packit Service a04d08
        # Do some basic key formatting
Packit Service a04d08
        repo_config = dict(
Packit Service a04d08
            (k.lower().strip().replace("-", "_"), v)
Packit Service a04d08
            for k, v in user_repo_config.items()
Packit Service a04d08
            if k and k != 'id')
Packit Service a04d08
Packit Service a04d08
        # Set defaults if not present
Packit Service a04d08
        for field in ['enabled', 'autorefresh']:
Packit Service a04d08
            if field not in repo_config:
Packit Service a04d08
                repo_config[field] = '1'
Packit Service a04d08
Packit Service a04d08
        valid_repos[repo_id] = (repo_fn_pth, repo_config)
Packit Service a04d08
Packit Service a04d08
    for (repo_id, repo_data) in valid_repos.items():
Packit Service a04d08
        repo_blob = _format_repository_config(repo_id, repo_data[-1])
Packit Service a04d08
        util.write_file(repo_data[0], repo_blob)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def _write_zypp_config(zypper_config):
Packit Service a04d08
    """Write to the default zypp configuration file /etc/zypp/zypp.conf"""
Packit Service a04d08
    if not zypper_config:
Packit Service a04d08
        return
Packit Service a04d08
    zypp_config = '/etc/zypp/zypp.conf'
Packit Service a04d08
    zypp_conf_content = util.load_file(zypp_config)
Packit Service a04d08
    new_settings = ['# Added via cloud.cfg']
Packit Service a04d08
    for setting, value in zypper_config.items():
Packit Service a04d08
        if setting == 'configdir':
Packit Service a04d08
            msg = 'Changing the location of the zypper configuration is '
Packit Service a04d08
            msg += 'not supported, skipping "configdir" setting'
Packit Service a04d08
            LOG.warning(msg)
Packit Service a04d08
            continue
Packit Service a04d08
        if value:
Packit Service a04d08
            new_settings.append('%s=%s' % (setting, value))
Packit Service a04d08
    if len(new_settings) > 1:
Packit Service a04d08
        new_config = zypp_conf_content + '\n'.join(new_settings)
Packit Service a04d08
    else:
Packit Service a04d08
        new_config = zypp_conf_content
Packit Service a04d08
    util.write_file(zypp_config, new_config)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def handle(name, cfg, _cloud, log, _args):
Packit Service a04d08
    zypper_section = cfg.get('zypper')
Packit Service a04d08
    if not zypper_section:
Packit Service a04d08
        LOG.debug(("Skipping module named %s,"
Packit Service a04d08
                   " no 'zypper' relevant configuration found"), name)
Packit Service a04d08
        return
Packit Service a04d08
    repos = zypper_section.get('repos')
Packit Service a04d08
    if not repos:
Packit Service a04d08
        LOG.debug(("Skipping module named %s,"
Packit Service a04d08
                   " no 'repos' configuration found"), name)
Packit Service a04d08
        return
Packit Service a04d08
    zypper_config = zypper_section.get('config', {})
Packit Service a04d08
    repo_base_path = zypper_config.get('reposdir', '/etc/zypp/repos.d/')
Packit Service a04d08
Packit Service a04d08
    _write_zypp_config(zypper_config)
Packit Service a04d08
    _write_repos(repos, repo_base_path)
Packit Service a04d08
Packit Service a04d08
# vi: ts=4 expandtab