|
Packit Service |
11b429 |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
# RELEASE_BLOCKER: Remove this deprecated module in 18.3
|
|
Packit Service |
11b429 |
"""
|
|
Packit Service |
11b429 |
Snappy
|
|
Packit Service |
11b429 |
------
|
|
Packit Service |
11b429 |
**Summary:** snappy modules allows configuration of snappy.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
**Deprecated**: Use :ref:`snap` module instead. This module will not exist
|
|
Packit Service |
11b429 |
in cloud-init 18.3.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
The below example config config would install ``etcd``, and then install
|
|
Packit Service |
11b429 |
``pkg2.smoser`` with a ``<config-file>`` argument where ``config-file`` has
|
|
Packit Service |
11b429 |
``config-blob`` inside it. If ``pkgname`` is installed already, then
|
|
Packit Service |
11b429 |
``snappy config pkgname <file>``
|
|
Packit Service |
11b429 |
will be called where ``file`` has ``pkgname-config-blob`` as its content.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
Entries in ``config`` can be namespaced or non-namespaced for a package.
|
|
Packit Service |
11b429 |
In either case, the config provided to snappy command is non-namespaced.
|
|
Packit Service |
11b429 |
The package name is provided as it appears.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
If ``packages_dir`` has files in it that end in ``.snap``, then they are
|
|
Packit Service |
11b429 |
installed. Given 3 files:
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
- <packages_dir>/foo.snap
|
|
Packit Service |
11b429 |
- <packages_dir>/foo.config
|
|
Packit Service |
11b429 |
- <packages_dir>/bar.snap
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
cloud-init will invoke:
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
- snappy install <packages_dir>/foo.snap <packages_dir>/foo.config
|
|
Packit Service |
11b429 |
- snappy install <packages_dir>/bar.snap
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
.. note::
|
|
Packit Service |
11b429 |
that if provided a ``config`` entry for ``ubuntu-core``, then
|
|
Packit Service |
11b429 |
cloud-init will invoke: snappy config ubuntu-core <config>
|
|
Packit Service |
11b429 |
Allowing you to configure ubuntu-core in this way.
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
The ``ssh_enabled`` key controls the system's ssh service. The default value
|
|
Packit Service |
11b429 |
is ``auto``. Options are:
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
- **True:** enable ssh service
|
|
Packit Service |
11b429 |
- **False:** disable ssh service
|
|
Packit Service |
11b429 |
- **auto:** enable ssh service if either ssh keys have been provided
|
|
Packit Service |
11b429 |
or user has requested password authentication (ssh_pwauth).
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
**Internal name:** ``cc_snappy``
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
**Module frequency:** per instance
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
**Supported distros:** ubuntu
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
**Config keys**::
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
#cloud-config
|
|
Packit Service |
11b429 |
snappy:
|
|
Packit Service |
11b429 |
system_snappy: auto
|
|
Packit Service |
11b429 |
ssh_enabled: auto
|
|
Packit Service |
11b429 |
packages: [etcd, pkg2.smoser]
|
|
Packit Service |
11b429 |
config:
|
|
Packit Service |
11b429 |
pkgname:
|
|
Packit Service |
11b429 |
key2: value2
|
|
Packit Service |
11b429 |
pkg2:
|
|
Packit Service |
11b429 |
key1: value1
|
|
Packit Service |
11b429 |
packages_dir: '/writable/user-data/cloud-init/snaps'
|
|
Packit Service |
11b429 |
"""
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
from cloudinit import log as logging
|
|
Packit Service |
11b429 |
from cloudinit.settings import PER_INSTANCE
|
|
Packit Service |
11b429 |
from cloudinit import temp_utils
|
|
Packit Service |
11b429 |
from cloudinit import safeyaml
|
|
Packit Service |
11b429 |
from cloudinit import util
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
import glob
|
|
Packit Service |
11b429 |
import os
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
LOG = logging.getLogger(__name__)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
frequency = PER_INSTANCE
|
|
Packit Service |
11b429 |
SNAPPY_CMD = "snappy"
|
|
Packit Service |
11b429 |
NAMESPACE_DELIM = '.'
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
BUILTIN_CFG = {
|
|
Packit Service |
11b429 |
'packages': [],
|
|
Packit Service |
11b429 |
'packages_dir': '/writable/user-data/cloud-init/snaps',
|
|
Packit Service |
11b429 |
'ssh_enabled': "auto",
|
|
Packit Service |
11b429 |
'system_snappy': "auto",
|
|
Packit Service |
11b429 |
'config': {},
|
|
Packit Service |
11b429 |
}
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
distros = ['ubuntu']
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def parse_filename(fname):
|
|
Packit Service |
11b429 |
fname = os.path.basename(fname)
|
|
Packit Service |
11b429 |
fname_noext = fname.rpartition(".")[0]
|
|
Packit Service |
11b429 |
name = fname_noext.partition("_")[0]
|
|
Packit Service |
11b429 |
shortname = name.partition(".")[0]
|
|
Packit Service |
11b429 |
return(name, shortname, fname_noext)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def get_fs_package_ops(fspath):
|
|
Packit Service |
11b429 |
if not fspath:
|
|
Packit Service |
11b429 |
return []
|
|
Packit Service |
11b429 |
ops = []
|
|
Packit Service |
11b429 |
for snapfile in sorted(glob.glob(os.path.sep.join([fspath, '*.snap']))):
|
|
Packit Service |
11b429 |
(name, shortname, fname_noext) = parse_filename(snapfile)
|
|
Packit Service |
11b429 |
cfg = None
|
|
Packit Service |
11b429 |
for cand in (fname_noext, name, shortname):
|
|
Packit Service |
11b429 |
fpcand = os.path.sep.join([fspath, cand]) + ".config"
|
|
Packit Service |
11b429 |
if os.path.isfile(fpcand):
|
|
Packit Service |
11b429 |
cfg = fpcand
|
|
Packit Service |
11b429 |
break
|
|
Packit Service |
11b429 |
ops.append(makeop('install', name, config=None,
|
|
Packit Service |
11b429 |
path=snapfile, cfgfile=cfg))
|
|
Packit Service |
11b429 |
return ops
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def makeop(op, name, config=None, path=None, cfgfile=None):
|
|
Packit Service |
11b429 |
return({'op': op, 'name': name, 'config': config, 'path': path,
|
|
Packit Service |
11b429 |
'cfgfile': cfgfile})
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def get_package_config(configs, name):
|
|
Packit Service |
11b429 |
# load the package's config from the configs dict.
|
|
Packit Service |
11b429 |
# prefer full-name entry (config-example.canonical)
|
|
Packit Service |
11b429 |
# over short name entry (config-example)
|
|
Packit Service |
11b429 |
if name in configs:
|
|
Packit Service |
11b429 |
return configs[name]
|
|
Packit Service |
11b429 |
return configs.get(name.partition(NAMESPACE_DELIM)[0])
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def get_package_ops(packages, configs, installed=None, fspath=None):
|
|
Packit Service |
11b429 |
# get the install an config operations that should be done
|
|
Packit Service |
11b429 |
if installed is None:
|
|
Packit Service |
11b429 |
installed = read_installed_packages()
|
|
Packit Service |
11b429 |
short_installed = [p.partition(NAMESPACE_DELIM)[0] for p in installed]
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
if not packages:
|
|
Packit Service |
11b429 |
packages = []
|
|
Packit Service |
11b429 |
if not configs:
|
|
Packit Service |
11b429 |
configs = {}
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
ops = []
|
|
Packit Service |
11b429 |
ops += get_fs_package_ops(fspath)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
for name in packages:
|
|
Packit Service |
11b429 |
ops.append(makeop('install', name, get_package_config(configs, name)))
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
to_install = [f['name'] for f in ops]
|
|
Packit Service |
11b429 |
short_to_install = [f['name'].partition(NAMESPACE_DELIM)[0] for f in ops]
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
for name in configs:
|
|
Packit Service |
11b429 |
if name in to_install:
|
|
Packit Service |
11b429 |
continue
|
|
Packit Service |
11b429 |
shortname = name.partition(NAMESPACE_DELIM)[0]
|
|
Packit Service |
11b429 |
if shortname in short_to_install:
|
|
Packit Service |
11b429 |
continue
|
|
Packit Service |
11b429 |
if name in installed or shortname in short_installed:
|
|
Packit Service |
11b429 |
ops.append(makeop('config', name,
|
|
Packit Service |
11b429 |
config=get_package_config(configs, name)))
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
# prefer config entries to filepath entries
|
|
Packit Service |
11b429 |
for op in ops:
|
|
Packit Service |
11b429 |
if op['op'] != 'install' or not op['cfgfile']:
|
|
Packit Service |
11b429 |
continue
|
|
Packit Service |
11b429 |
name = op['name']
|
|
Packit Service |
11b429 |
fromcfg = get_package_config(configs, op['name'])
|
|
Packit Service |
11b429 |
if fromcfg:
|
|
Packit Service |
11b429 |
LOG.debug("preferring configs[%(name)s] over '%(cfgfile)s'", op)
|
|
Packit Service |
11b429 |
op['cfgfile'] = None
|
|
Packit Service |
11b429 |
op['config'] = fromcfg
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
return ops
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def render_snap_op(op, name, path=None, cfgfile=None, config=None):
|
|
Packit Service |
11b429 |
if op not in ('install', 'config'):
|
|
Packit Service |
11b429 |
raise ValueError("cannot render op '%s'" % op)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
shortname = name.partition(NAMESPACE_DELIM)[0]
|
|
Packit Service |
11b429 |
try:
|
|
Packit Service |
11b429 |
cfg_tmpf = None
|
|
Packit Service |
11b429 |
if config is not None:
|
|
Packit Service |
11b429 |
# input to 'snappy config packagename' must have nested data. odd.
|
|
Packit Service |
11b429 |
# config:
|
|
Packit Service |
11b429 |
# packagename:
|
|
Packit Service |
11b429 |
# config
|
|
Packit Service |
11b429 |
# Note, however, we do not touch config files on disk.
|
|
Packit Service |
11b429 |
nested_cfg = {'config': {shortname: config}}
|
|
Packit Service |
11b429 |
(fd, cfg_tmpf) = temp_utils.mkstemp()
|
|
Packit Service |
11b429 |
os.write(fd, safeyaml.dumps(nested_cfg).encode())
|
|
Packit Service |
11b429 |
os.close(fd)
|
|
Packit Service |
11b429 |
cfgfile = cfg_tmpf
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
cmd = [SNAPPY_CMD, op]
|
|
Packit Service |
11b429 |
if op == 'install':
|
|
Packit Service |
11b429 |
if path:
|
|
Packit Service |
11b429 |
cmd.append("--allow-unauthenticated")
|
|
Packit Service |
11b429 |
cmd.append(path)
|
|
Packit Service |
11b429 |
else:
|
|
Packit Service |
11b429 |
cmd.append(name)
|
|
Packit Service |
11b429 |
if cfgfile:
|
|
Packit Service |
11b429 |
cmd.append(cfgfile)
|
|
Packit Service |
11b429 |
elif op == 'config':
|
|
Packit Service |
11b429 |
cmd += [name, cfgfile]
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
util.subp(cmd)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
finally:
|
|
Packit Service |
11b429 |
if cfg_tmpf:
|
|
Packit Service |
11b429 |
os.unlink(cfg_tmpf)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def read_installed_packages():
|
|
Packit Service |
11b429 |
ret = []
|
|
Packit Service |
11b429 |
for (name, _date, _version, dev) in read_pkg_data():
|
|
Packit Service |
11b429 |
if dev:
|
|
Packit Service |
11b429 |
ret.append(NAMESPACE_DELIM.join([name, dev]))
|
|
Packit Service |
11b429 |
else:
|
|
Packit Service |
11b429 |
ret.append(name)
|
|
Packit Service |
11b429 |
return ret
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def read_pkg_data():
|
|
Packit Service |
11b429 |
out, _err = util.subp([SNAPPY_CMD, "list"])
|
|
Packit Service |
11b429 |
pkg_data = []
|
|
Packit Service |
11b429 |
for line in out.splitlines()[1:]:
|
|
Packit Service |
11b429 |
toks = line.split(sep=None, maxsplit=3)
|
|
Packit Service |
11b429 |
if len(toks) == 3:
|
|
Packit Service |
11b429 |
(name, date, version) = toks
|
|
Packit Service |
11b429 |
dev = None
|
|
Packit Service |
11b429 |
else:
|
|
Packit Service |
11b429 |
(name, date, version, dev) = toks
|
|
Packit Service |
11b429 |
pkg_data.append((name, date, version, dev,))
|
|
Packit Service |
11b429 |
return pkg_data
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def disable_enable_ssh(enabled):
|
|
Packit Service |
11b429 |
LOG.debug("setting enablement of ssh to: %s", enabled)
|
|
Packit Service |
11b429 |
# do something here that would enable or disable
|
|
Packit Service |
11b429 |
not_to_be_run = "/etc/ssh/sshd_not_to_be_run"
|
|
Packit Service |
11b429 |
if enabled:
|
|
Packit Service |
11b429 |
util.del_file(not_to_be_run)
|
|
Packit Service |
11b429 |
# this is an indempotent operation
|
|
Packit Service |
11b429 |
util.subp(["systemctl", "start", "ssh"])
|
|
Packit Service |
11b429 |
else:
|
|
Packit Service |
11b429 |
# this is an indempotent operation
|
|
Packit Service |
11b429 |
util.subp(["systemctl", "stop", "ssh"])
|
|
Packit Service |
11b429 |
util.write_file(not_to_be_run, "cloud-init\n")
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def set_snappy_command():
|
|
Packit Service |
11b429 |
global SNAPPY_CMD
|
|
Packit Service |
11b429 |
if util.which("snappy-go"):
|
|
Packit Service |
11b429 |
SNAPPY_CMD = "snappy-go"
|
|
Packit Service |
11b429 |
elif util.which("snappy"):
|
|
Packit Service |
11b429 |
SNAPPY_CMD = "snappy"
|
|
Packit Service |
11b429 |
else:
|
|
Packit Service |
11b429 |
SNAPPY_CMD = "snap"
|
|
Packit Service |
11b429 |
LOG.debug("snappy command is '%s'", SNAPPY_CMD)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
def handle(name, cfg, cloud, log, args):
|
|
Packit Service |
11b429 |
cfgin = cfg.get('snappy')
|
|
Packit Service |
11b429 |
if not cfgin:
|
|
Packit Service |
11b429 |
cfgin = {}
|
|
Packit Service |
11b429 |
mycfg = util.mergemanydict([cfgin, BUILTIN_CFG])
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
sys_snappy = str(mycfg.get("system_snappy", "auto"))
|
|
Packit Service |
11b429 |
if util.is_false(sys_snappy):
|
|
Packit Service |
11b429 |
LOG.debug("%s: System is not snappy. disabling", name)
|
|
Packit Service |
11b429 |
return
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
if sys_snappy.lower() == "auto" and not(util.system_is_snappy()):
|
|
Packit Service |
11b429 |
LOG.debug("%s: 'auto' mode, and system not snappy", name)
|
|
Packit Service |
11b429 |
return
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
log.warning(
|
|
Packit Service |
11b429 |
'DEPRECATION: snappy module will be dropped in 18.3 release.'
|
|
Packit Service |
11b429 |
' Use snap module instead')
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
set_snappy_command()
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
pkg_ops = get_package_ops(packages=mycfg['packages'],
|
|
Packit Service |
11b429 |
configs=mycfg['config'],
|
|
Packit Service |
11b429 |
fspath=mycfg['packages_dir'])
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
fails = []
|
|
Packit Service |
11b429 |
for pkg_op in pkg_ops:
|
|
Packit Service |
11b429 |
try:
|
|
Packit Service |
11b429 |
render_snap_op(**pkg_op)
|
|
Packit Service |
11b429 |
except Exception as e:
|
|
Packit Service |
11b429 |
fails.append((pkg_op, e,))
|
|
Packit Service |
11b429 |
LOG.warning("'%s' failed for '%s': %s",
|
|
Packit Service |
11b429 |
pkg_op['op'], pkg_op['name'], e)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
# Default to disabling SSH
|
|
Packit Service |
11b429 |
ssh_enabled = mycfg.get('ssh_enabled', "auto")
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
# If the user has not explicitly enabled or disabled SSH, then enable it
|
|
Packit Service |
11b429 |
# when password SSH authentication is requested or there are SSH keys
|
|
Packit Service |
11b429 |
if ssh_enabled == "auto":
|
|
Packit Service |
11b429 |
user_ssh_keys = cloud.get_public_ssh_keys() or None
|
|
Packit Service |
11b429 |
password_auth_enabled = cfg.get('ssh_pwauth', False)
|
|
Packit Service |
11b429 |
if user_ssh_keys:
|
|
Packit Service |
11b429 |
LOG.debug("Enabling SSH, ssh keys found in datasource")
|
|
Packit Service |
11b429 |
ssh_enabled = True
|
|
Packit Service |
11b429 |
elif cfg.get('ssh_authorized_keys'):
|
|
Packit Service |
11b429 |
LOG.debug("Enabling SSH, ssh keys found in config")
|
|
Packit Service |
11b429 |
elif password_auth_enabled:
|
|
Packit Service |
11b429 |
LOG.debug("Enabling SSH, password authentication requested")
|
|
Packit Service |
11b429 |
ssh_enabled = True
|
|
Packit Service |
11b429 |
elif ssh_enabled not in (True, False):
|
|
Packit Service |
11b429 |
LOG.warning("Unknown value '%s' in ssh_enabled", ssh_enabled)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
disable_enable_ssh(ssh_enabled)
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
if fails:
|
|
Packit Service |
11b429 |
raise Exception("failed to install/configure snaps")
|
|
Packit Service |
11b429 |
|
|
Packit Service |
11b429 |
# vi: ts=4 expandtab
|