← Back to team overview

cloud-init-dev team mailing list archive

[Merge] lp:~harlowja/cloud-init/cloud-init-seedy into lp:cloud-init

 

Joshua Harlow has proposed merging lp:~harlowja/cloud-init/cloud-init-seedy into lp:cloud-init.

Requested reviews:
  cloud init development team (cloud-init-dev)
Related bugs:
  Bug #1198297 in cloud-init: "RFE: seed /dev/urandom with random data from metadata service when available"
  https://bugs.launchpad.net/cloud-init/+bug/1198297

For more details, see:
https://code.launchpad.net/~harlowja/cloud-init/cloud-init-seedy/+merge/183571

  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.
-- 
https://code.launchpad.net/~harlowja/cloud-init/cloud-init-seedy/+merge/183571
Your team cloud init development team is requested to review the proposed merge of lp:~harlowja/cloud-init/cloud-init-seedy into lp:cloud-init.
=== modified file 'cloudinit/distros/__init__.py'
--- cloudinit/distros/__init__.py	2013-07-25 05:12:33 +0000
+++ cloudinit/distros/__init__.py	2013-09-03 05:53:42 +0000
@@ -52,6 +52,7 @@
     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
 
     def __init__(self, name, cfg, paths):
         self._paths = paths
@@ -169,6 +170,11 @@
             distros.extend(OSFAMILIES[family])
         return distros
 
+    def set_random_seed(self, seed):
+        if self.random_seed_fn:
+            # Ensure we only write 512 bytes worth
+            util.write_file(self.random_seed_fn, seed[0:512], mode=0600)
+
     def update_hostname(self, hostname, fqdn, prev_hostname_fn):
         applying_hostname = hostname
 

=== modified file 'cloudinit/distros/debian.py'
--- cloudinit/distros/debian.py	2013-07-25 05:12:33 +0000
+++ cloudinit/distros/debian.py	2013-09-03 05:53:42 +0000
@@ -44,6 +44,7 @@
     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)

=== modified file 'cloudinit/distros/rhel.py'
--- cloudinit/distros/rhel.py	2013-08-06 10:36:30 +0000
+++ cloudinit/distros/rhel.py	2013-09-03 05:53:42 +0000
@@ -49,6 +49,7 @@
     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)

=== modified file 'cloudinit/sources/DataSourceConfigDrive.py'
--- cloudinit/sources/DataSourceConfigDrive.py	2013-06-05 00:42:55 +0000
+++ cloudinit/sources/DataSourceConfigDrive.py	2013-09-03 05:53:42 +0000
@@ -18,6 +18,7 @@
 #    You should have received a copy of the GNU General Public License
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import base64
 import json
 import os
 
@@ -41,6 +42,32 @@
 VALID_DSMODES = ("local", "net", "pass", "disabled")
 
 
+class ConfigDriveHelper(object):
+    def __init__(self, distro):
+        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'])
+        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 +76,7 @@
         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 +215,8 @@
         # 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 +354,13 @@
         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",


Follow ups