From ee9e56a9d286d34b20bbe3de200872d1eee1af38 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 2 Sep 2013 22:43:51 -0700 Subject: Add config drive support for random_seed A new field in the metadata has emerged, one that provides a way to seed the linux random generator. Add support for writing the seed and rewrite parts of the on_boot code to use a little helper class. LP: #1198297 --- cloudinit/sources/DataSourceConfigDrive.py | 49 +++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'cloudinit/sources/DataSourceConfigDrive.py') diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 835f2a9a..23f791aa 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import base64 import json import os @@ -41,6 +42,30 @@ DEFAULT_METADATA = { VALID_DSMODES = ("local", "net", "pass", "disabled") +class ConfigDriveHelper(object): + def __init__(self, distro): + self.distro = distro + + def on_first_boot(self, data): + if 'network_config' in data: + LOG.debug("Updating network interfaces from config drive") + self.distro.apply_network(data['network_config']) + files = data.get('files') + if files: + LOG.debug("Writing %s injected files", len(files)) + try: + write_files(files) + except IOError: + util.logexc(LOG, "Failed writing files") + random_seed = util.get_cfg_by_path(data, ('metadata', 'random_seed')) + if random_seed is not None: + LOG.debug("Writing random seed") + try: + self.distro.set_random_seed(random_seed) + except IOError: + util.logexc(LOG, "Failed writing random seed") + + class DataSourceConfigDrive(sources.DataSource): def __init__(self, sys_cfg, distro, paths): sources.DataSource.__init__(self, sys_cfg, distro, paths) @@ -49,6 +74,7 @@ class DataSourceConfigDrive(sources.DataSource): self.seed_dir = os.path.join(paths.seed_dir, 'config_drive') self.version = None self.ec2_metadata = None + self.helper = ConfigDriveHelper(distro) def __str__(self): root = sources.DataSource.__str__(self) @@ -187,20 +213,8 @@ class DataSourceConfigDrive(sources.DataSource): # instance-id prev_iid = get_previous_iid(self.paths) cur_iid = md['instance-id'] - - if ('network_config' in results and self.dsmode == "local" and - prev_iid != cur_iid): - LOG.debug("Updating network interfaces from config drive (%s)", - dsmode) - self.distro.apply_network(results['network_config']) - - # file writing occurs in local mode (to be as early as possible) - if self.dsmode == "local" and prev_iid != cur_iid and results['files']: - LOG.debug("writing injected files") - try: - write_files(results['files']) - except: - util.logexc(LOG, "Failed writing files") + if prev_iid != cur_iid and self.dsmode == "local": + self.helper.on_first_boot(results) # dsmode != self.dsmode here if: # * dsmode = "pass", pass means it should only copy files and then @@ -338,6 +352,13 @@ def read_config_drive_dir_v2(source_dir, version="2012-08-10"): except KeyError: raise BrokenConfigDriveDir("No uuid entry in metadata") + if 'random_seed' in results['metadata']: + random_seed = results['metadata']['random_seed'] + try: + results['metadata']['random_seed'] = base64.b64decode(random_seed) + except (ValueError, TypeError) as exc: + raise BrokenConfigDriveDir("Badly formatted random_seed: %s" % exc) + def read_content_path(item): # do not use os.path.join here, as content_path starts with / cpath = os.path.sep.join((source_dir, "openstack", -- cgit v1.2.3 From 73863ed53d87e0eb3c24449ef164089531bc9679 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 2 Sep 2013 22:46:03 -0700 Subject: Ensure data is initialized to a dict if its empty/none LP: #1198297 --- cloudinit/sources/DataSourceConfigDrive.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'cloudinit/sources/DataSourceConfigDrive.py') diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 23f791aa..af205d86 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -47,6 +47,8 @@ class ConfigDriveHelper(object): self.distro = distro def on_first_boot(self, data): + if not data: + data = {} if 'network_config' in data: LOG.debug("Updating network interfaces from config drive") self.distro.apply_network(data['network_config']) -- cgit v1.2.3 From c3e070de802ebc0f44722d4238f5447b93cc9fac Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 3 Sep 2013 23:51:51 -0700 Subject: Review adjustments. --- cloudinit/config/cc_seed_random.py | 36 ++++++++++++++++++++++++++++++ cloudinit/distros/__init__.py | 8 ++++--- cloudinit/distros/debian.py | 1 - cloudinit/distros/rhel.py | 1 - cloudinit/sources/DataSourceConfigDrive.py | 7 ------ config/cloud.cfg | 1 + 6 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 cloudinit/config/cc_seed_random.py (limited to 'cloudinit/sources/DataSourceConfigDrive.py') diff --git a/cloudinit/config/cc_seed_random.py b/cloudinit/config/cc_seed_random.py new file mode 100644 index 00000000..5d9890d5 --- /dev/null +++ b/cloudinit/config/cc_seed_random.py @@ -0,0 +1,36 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2013 Yahoo! Inc. +# +# Author: Joshua Harlow +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from cloudinit.settings import PER_INSTANCE + +frequency = PER_INSTANCE + + +def handle(name, cfg, cloud, log, _args): + random_seed = None + # Prefer metadata over cfg for random_seed + for src in (cloud.datasource.metadata, cfg): + if not src: + continue + tmp_random_seed = src.get('random_seed') + if tmp_random_seed and isinstance(tmp_random_seed, (str, basestring)): + random_seed = tmp_random_seed + break + if random_seed: + log.debug("%s: setting random seed", name) + cloud.distro.set_random_seed(random_seed) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 8cffb0ee..5642b529 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -52,7 +52,7 @@ class Distro(object): ci_sudoers_fn = "/etc/sudoers.d/90-cloud-init-users" hostname_conf_fn = "/etc/hostname" tz_zone_dir = "/usr/share/zoneinfo" - random_seed_fn = None + random_seed_fn = '/dev/urandom' def __init__(self, name, cfg, paths): self._paths = paths @@ -171,11 +171,13 @@ class Distro(object): return distros def set_random_seed(self, seed): - if not self.random_seed_fn: + if not self.random_seed_fn or not os.path.exists(self.random_seed_fn): raise IOError("No random seed filename provided for %s" % (self.name)) + if not seed: + raise IOError("Unable to set empty random seed") # Ensure we only write 512 bytes worth - util.write_file(self.random_seed_fn, seed[0:512], mode=0600) + util.append_file(self.random_seed_fn, seed[0:512]) def update_hostname(self, hostname, fqdn, prev_hostname_fn): applying_hostname = hostname diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index db4afc76..8fe49cbe 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -44,7 +44,6 @@ class Distro(distros.Distro): network_conf_fn = "/etc/network/interfaces" tz_conf_fn = "/etc/timezone" tz_local_fn = "/etc/localtime" - random_seed_fn = "/var/lib/urandom/random-seed" def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 96df9ae2..30195384 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -49,7 +49,6 @@ class Distro(distros.Distro): network_script_tpl = '/etc/sysconfig/network-scripts/ifcfg-%s' resolve_conf_fn = "/etc/resolv.conf" tz_local_fn = "/etc/localtime" - random_seed_fn = "/var/lib/random-seed" def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index af205d86..4f437244 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -59,13 +59,6 @@ class ConfigDriveHelper(object): write_files(files) except IOError: util.logexc(LOG, "Failed writing files") - random_seed = util.get_cfg_by_path(data, ('metadata', 'random_seed')) - if random_seed is not None: - LOG.debug("Writing random seed") - try: - self.distro.set_random_seed(random_seed) - except IOError: - util.logexc(LOG, "Failed writing random seed") class DataSourceConfigDrive(sources.DataSource): diff --git a/config/cloud.cfg b/config/cloud.cfg index b61b8a7d..cce1f376 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -24,6 +24,7 @@ preserve_hostname: false # The modules that run in the 'init' stage cloud_init_modules: - migrator + - seed_random - bootcmd - write-files - growpart -- cgit v1.2.3