1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
# vi: ts=4 expandtab
#
from cloudinit import log as logging
from cloudinit import templater
from cloudinit import util
from cloudinit.settings import PER_INSTANCE
import glob
import os
LOG = logging.getLogger(__name__)
frequency = PER_INSTANCE
SNAPPY_ENV_PATH = "/writable/system-data/etc/snappy.env"
CI_SNAPPY_CFG = {
'env_file_path': SNAPPY_ENV_PATH,
'packages': [],
'packages_dir': '/writable/user-data/cloud-init/click_packages',
'ssh_enabled': False
}
"""
snappy:
ssh_enabled: True
packages:
- etcd
- {'name': 'pkg1', 'config': "wark"}
"""
def flatten(data, fill=None, tok="_", prefix='', recurse=True):
if fill is None:
fill = {}
for key, val in data.items():
key = key.replace("-", "_")
if isinstance(val, dict) and recurse:
flatten(val, fill, tok=tok, prefix=prefix + key + tok,
recurse=recurse)
elif isinstance(key, str):
fill[prefix + key] = val
return fill
def render2env(data, tok="_", prefix=''):
flat = flatten(data, tok=tok, prefix=prefix)
ret = ["%s='%s'" % (key, val) for key, val in flat.items()]
return '\n'.join(ret) + '\n'
def install_package(pkg_name, config=None):
cmd = ["snappy", "install"]
if config:
if os.path.isfile(config):
cmd.append("--config-file=" + config)
else:
cmd.append("--config=" + config)
cmd.append(pkg_name)
util.subp(cmd)
def install_packages(package_dir, packages):
local_pkgs = glob.glob(os.path.sep.join([package_dir, '*.click']))
LOG.debug("installing local packages %s" % local_pkgs)
if local_pkgs:
for pkg in local_pkgs:
cfg = pkg.replace(".click", ".config")
if not os.path.isfile(cfg):
cfg = None
install_package(pkg, config=cfg)
LOG.debug("installing click packages")
if packages:
for pkg in packages:
if not pkg:
continue
if isinstance(pkg, str):
name = pkg
config = None
elif pkg:
name = pkg.get('name', pkg)
config = pkg.get('config')
install_package(pkg_name=name, config=config)
def disable_enable_ssh(enabled):
LOG.debug("setting enablement of ssh to: %s", enabled)
# do something here that would enable or disable
not_to_be_run = "/etc/ssh/sshd_not_to_be_run"
if enabled:
util.del_file(not_to_be_run)
# this is an indempotent operation
util.subp(["systemctl", "start", "ssh"])
else:
# this is an indempotent operation
util.subp(["systemctl", "stop", "ssh"])
util.write_file(not_to_be_run, "cloud-init\n")
def handle(name, cfg, cloud, log, args):
mycfg = cfg.get('snappy', {'ssh_enabled': False})
if not mycfg:
LOG.debug("%s: no top level found", name)
return
# take out of 'cfg' the cfg keys that cloud-init uses, so
# mycfg has only content external to cloud-init.
ci_cfg = CI_SNAPPY_CFG.copy()
for i in ci_cfg:
if i in mycfg:
ci_cfg[i] = mycfg[i]
del mycfg[i]
# render the flattened environment variable style file to a path
# this was useful for systemd config environment files. given:
# snappy:
# foo:
# bar: wark
# cfg1:
# key1: value
# you get the following in env_file_path.
# foo_bar=wark
# foo_cfg1_key1=value
contents = render2env(mycfg)
header = '# for internal use only, not a guaranteed interface\n'
util.write_file(ci_cfg['env_file_path'], header + render2env(mycfg))
install_packages(ci_cfg['packages_dir'],
ci_cfg['packages'])
disable_enable_ssh(ci_cfg.get('ssh_enabled', False))
|