From 54346d35221fd405423dd33a2b06202f10e2aa22 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 12 Aug 2010 01:56:12 -0400 Subject: initial dump of "sans-cloud" code (DataSourceNoCloud) The new classes 'DataSourceNoCloud' and 'DataSourceNoCloudNet' implement a way to get data from the filesystem, or (very minimal) data from the kernel command line. This allows the user to seed data to these sources. There are now 2 "cloud-init" jobs, cloud-init-local that runs on mounted MOUNTPOINT=/ and 'cloud-init' that runs on start on (mounted MOUNTPOINT=/ and net-device-up IFACE=eth0 and stopped cloud-init-local ) The idea is that cloud-init-local can actually function without network. The last thing in this commit is "uncloud-init". This tool can be invoked as 'init=/usr/lib/cloud-init/uncloud-init' It will "uncloudify" things in the image, generally making it easier to use for a simpler environment, and then it will exec /sbin/init. --- cloudinit/DataSourceNoCloud.py | 152 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 cloudinit/DataSourceNoCloud.py (limited to 'cloudinit/DataSourceNoCloud.py') diff --git a/cloudinit/DataSourceNoCloud.py b/cloudinit/DataSourceNoCloud.py new file mode 100644 index 00000000..573f3f43 --- /dev/null +++ b/cloudinit/DataSourceNoCloud.py @@ -0,0 +1,152 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2009-2010 Canonical Ltd. +# +# Author: Scott Moser +# +# 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 . + +import DataSource + +import cloudinit +import cloudinit.util as util +import sys +import os.path +import os +import errno + +class DataSourceNoCloud(DataSource.DataSource): + metadata = None + userdata = None + userdata_raw = None + supported_seed_starts = ( "/" , "file://" ) + seed = None + cmdline_id = "ds=nocloud" + seeddir = cloudinit.cachedir + '/nocloud' + + def __init__(self): + pass + + def __str__(self): + mstr="DataSourceNoCloud" + mstr = mstr + " [seed=%s]" % self.seed + return(mstr) + + def get_data(self): + defaults = { + "local-hostname" : "ubuntuhost", + "instance-id" : "nocloud" + } + + md = { } + ud = "" + + try: + # parse the kernel command line, getting data passed in + md = parse_cmdline_data(self.cmdline_id) + except: + util.logexc(cloudinit.log,util.WARN) + return False + + # check to see if the seeddir has data. + try: + (seeddir_md,ud) = util.read_seeded(self.seeddir + "/") + self.metadata = md + md = util.mergedict(md,seeddir_md) + cloudinit.log.debug("using seeded cache data in %s" % self.seeddir) + except OSError, e: + if e.errno != errno.ENOENT: + util.logexc(cloudinit.log,util.WARN) + raise + + # there was no indication on kernel cmdline or data + # in the seeddir suggesting this handler should be used. + if md is None: + return False + + # the special argument "seedfrom" indicates we should + # attempt to seed the userdata / metadata from its value + if "seedfrom" in md: + seedfrom = md["seedfrom"] + found = False + for proto in self.supported_seed_starts: + if seedfrom.startswith(proto): + found=proto + break + if not found: + cloudinit.log.debug("seed from %s not supported by %s" % + (seedfrom, self.__class__)) + return False + + # this could throw errors, but the user told us to do it + # so if errors are raised, let them raise + (md_seed,ud) = util.read_seeded(seedfrom) + cloudinit.log.debug("using seeded cache data from %s" % seedfrom) + + # values in the command line override those from the seed + md = util.mergedict(md,md_seed) + self.seed = seedfrom + + md = util.mergedict(md,defaults) + self.metadata = md; + self.userdata_raw = ud + return True + +# returns a dictionary of key/val or None if cmdline did not have data +# example cmdline: +# root=LABEL=uec-rootfs ro ds=nocloud +def parse_cmdline_data(ds_id,cmdline=None): + if cmdline is None: + if 'DEBUG_PROC_CMDLINE' in os.environ: + cmdline = os.environ["DEBUG_PROC_CMDLINE"] + else: + cmdfp = open("/proc/cmdline") + cmdline = cmdfp.read().strip() + cmdfp.close() + cmdline = " %s " % cmdline.lower() + + if not ( " %s " % ds_id in cmdline or " %s;" % ds_id in cmdline ): + return None + + argline="" + # cmdline can contain: + # ds=nocloud[;key=val;key=val] + for tok in cmdline.split(): + if tok.startswith(ds_id): argline=tok.split("=",1) + + # argline array is now 'nocloud' followed optionally by + # a ';' and then key=value pairs also terminated with ';' + tmp=argline[1].split(";") + if len(tmp) > 1: + kvpairs=tmp[1:] + else: + kvpairs=() + + # short2long mapping to save cmdline typing + s2l = { "h" : "local-hostname", "i" : "instance-id", "s" : "seedfrom" } + cfg = { } + for item in kvpairs: + try: + (k,v) = item.split("=",1) + except: + k=item + v=None + if k in s2l: k=s2l[k] + cfg[k]=v + + return(cfg) + +class DataSourceNoCloudNet(DataSourceNoCloud): + cmdline_id = "ds=nocloud-net" + supported_seed_starts = ( "http://", "https://", "ftp://" ) + seeddir = cloudinit.cachedir + '/nocloud-net' -- cgit v1.2.3