summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcloud-init.py31
-rw-r--r--cloudinit/DataSource.py41
-rw-r--r--cloudinit/DataSourceEc2.py51
-rw-r--r--cloudinit/DataSourceNoCloud.py152
-rw-r--r--cloudinit/__init__.py28
-rw-r--r--cloudinit/util.py33
-rw-r--r--doc/examples/preseed-metadata.txt24
-rwxr-xr-xsetup.py2
-rwxr-xr-xtools/uncloud-init138
-rw-r--r--upstart/cloud-init-local.conf9
-rw-r--r--upstart/cloud-init.conf3
11 files changed, 459 insertions, 53 deletions
diff --git a/cloud-init.py b/cloud-init.py
index ff70017f..11bf89af 100755
--- a/cloud-init.py
+++ b/cloud-init.py
@@ -30,6 +30,15 @@ def warn(str):
sys.stderr.write(str)
def main():
+ cmds = ( "start", "start-local" )
+ cmd = ""
+ if len(sys.argv) > 1:
+ cmd = sys.argv[1]
+
+ if not cmd in cmds:
+ sys.stderr.write("bad command %s. use one of %s\n" % (cmd, cmds))
+ sys.exit(1)
+
now = time.strftime("%a, %d %b %Y %H:%M:%S %z")
try:
uptimef=open("/proc/uptime")
@@ -39,29 +48,39 @@ def main():
warn("unable to open /proc/uptime\n")
uptime = "na"
- msg = "cloud-init running: %s. up %s seconds" % (now, uptime)
+ msg = "cloud-init %s running: %s. up %s seconds" % (cmd, now, uptime)
sys.stderr.write(msg + "\n")
sys.stderr.flush()
+ source_type = "all"
+ if cmd == "start-local":
+ source_type = "local"
+
cloudinit.logging_set_from_cfg_file()
log = logging.getLogger()
log.info(msg)
# cache is not instance specific, so it has to be purged
- cloudinit.purge_cache()
+ # but we want 'start' to benefit from a cache if
+ # a previous start-local populated one
+ if cmd == "start-local":
+ cloudinit.purge_cache()
- cloud = cloudinit.CloudInit()
+ cloud = cloudinit.CloudInit(source_type=source_type)
try:
cloud.get_data_source()
- except Exception as e:
- print e
- sys.stderr.write("Failed to get instance data\n")
+ except cloudinit.DataSourceNotFoundException as e:
+ sys.stderr.write("no instance data found in %s\n" % cmd)
sys.exit(1)
# store the metadata
cloud.update_cache()
+ msg = "found data source: %s" % cloud.datasource
+ sys.stderr.write(msg + "\n")
+ log.debug(msg)
+
# parse the user data (ec2-run-userdata.py)
try:
cloud.sem_and_run("consume_userdata", "once-per-instance",
diff --git a/cloudinit/DataSource.py b/cloudinit/DataSource.py
index 858608cc..d1458ffd 100644
--- a/cloudinit/DataSource.py
+++ b/cloudinit/DataSource.py
@@ -36,7 +36,20 @@ class DataSource:
return(self.userdata_raw)
def get_public_ssh_keys(self):
- return([])
+ keys = []
+ if not self.metadata.has_key('public-keys'): return([])
+ for keyname, klist in self.metadata['public-keys'].items():
+ # lp:506332 uec metadata service responds with
+ # data that makes boto populate a string for 'klist' rather
+ # than a list.
+ if isinstance(klist,str):
+ klist = [ klist ]
+ for pkey in klist:
+ # there is an empty string at the end of the keylist, trim it
+ if pkey:
+ keys.append(pkey)
+
+ return(keys)
def device_name_to_device(self, name):
# translate a 'name' to a device
@@ -45,3 +58,29 @@ class DataSource:
# ephemeral0: sdb
# and return 'sdb' for input 'ephemeral0'
return(None)
+
+ def get_locale(self):
+ return('en_US.UTF-8')
+
+ def get_local_mirror(self):
+ return('http://archive.ubuntu.com/ubuntu/')
+
+ def get_instance_id(self):
+ if 'instance-id' not in self.metadata:
+ return "ubuntuhost"
+ return(self.metadata['instance-id'])
+
+ def get_hostname(self):
+ if not 'local-hostname' in self.metadata:
+ return None
+
+ toks = self.metadata['local-hostname'].split('.')
+ # if there is an ipv4 address in 'local-hostname', then
+ # make up a hostname (LP: #475354)
+ if len(toks) == 4:
+ try:
+ r = filter(lambda x: int(x) < 256 and x > 0, toks)
+ if len(r) == 4:
+ return("ip-%s" % '-'.join(r))
+ except: pass
+ return toks[0]
diff --git a/cloudinit/DataSourceEc2.py b/cloudinit/DataSourceEc2.py
index cf50a3d5..8a79eb5f 100644
--- a/cloudinit/DataSourceEc2.py
+++ b/cloudinit/DataSourceEc2.py
@@ -19,12 +19,14 @@
import DataSource
import cloudinit
+import cloudinit.util as util
import socket
import urllib2
import time
import sys
import boto_utils
import os.path
+import errno
class DataSourceEc2(DataSource.DataSource):
api_ver = '2009-04-04'
@@ -39,21 +41,20 @@ class DataSourceEc2(DataSource.DataSource):
def __init__(self):
pass
+ def __str__(self):
+ return("DataSourceEc2")
+
def get_data(self):
try:
- udf = open(self.cachedir + "/user-data.raw")
- self.userdata_raw = udf.read()
- udf.close()
-
- mdf = open(self.cachedir + "/meta-data.raw")
- data = mdf.read()
- self.metadata = eval(data)
- mdf.close()
-
+ (md,ud) = util.read_seeded(self.cachedir + "/")
+ self.userdata_raw = ud
+ self.metadata = md
cloudinit.log.debug("using seeded ec2 cache data in %s" % self.cachedir)
return True
- except:
- pass
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ cloudinit.log.warn("unexpected error from preseeded data")
+ raise
try:
if not self.wait_for_metadata_service():
@@ -81,18 +82,6 @@ class DataSourceEc2(DataSource.DataSource):
else:
return(self.location_locale_map["default"])
- def get_hostname(self):
- toks = self.metadata['local-hostname'].split('.')
- # if there is an ipv4 address in 'local-hostname', then
- # make up a hostname (LP: #475354)
- if len(toks) == 4:
- try:
- r = filter(lambda x: int(x) < 256 and x > 0, toks)
- if len(r) == 4:
- return("ip-%s" % '-'.join(r))
- except: pass
- return toks[0]
-
def get_mirror_from_availability_zone(self, availability_zone = None):
# availability is like 'us-west-1b' or 'eu-west-1a'
if availability_zone == None:
@@ -138,22 +127,6 @@ class DataSourceEc2(DataSource.DataSource):
int(time.time()-starttime))
return False
- def get_public_ssh_keys(self):
- keys = []
- if not self.metadata.has_key('public-keys'): return([])
- for keyname, klist in self.metadata['public-keys'].items():
- # lp:506332 uec metadata service responds with
- # data that makes boto populate a string for 'klist' rather
- # than a list.
- if isinstance(klist,str):
- klist = [ klist ]
- for pkey in klist:
- # there is an empty string at the end of the keylist, trim it
- if pkey:
- keys.append(pkey)
-
- return(keys)
-
def device_name_to_device(self, name):
# consult metadata service, that has
# ephemeral0: sdb
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 <scott.moser@canonical.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+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'
diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py
index a5df3436..830bc17b 100644
--- a/cloudinit/__init__.py
+++ b/cloudinit/__init__.py
@@ -159,20 +159,27 @@ def logging_set_from_cfg(cfg, logfile=None):
logging.config.fileConfig(StringIO.StringIO(failsafe))
import DataSourceEc2
+import DataSourceNoCloud
import UserDataHandler
class CloudInit:
datasource_map = {
"ec2" : DataSourceEc2.DataSourceEc2,
+ "nocloud" : DataSourceNoCloud.DataSourceNoCloud,
+ "nocloud-net" : DataSourceNoCloud.DataSourceNoCloudNet
}
datasource = None
- auto_order = [ 'ec2' ]
+ auto_orders = {
+ "all": ( "nocloud-net", "ec2" ),
+ "local" : ( "nocloud", ),
+ }
cfg = None
part_handlers = { }
old_conffile = '/etc/ec2-init/ec2-config.cfg'
+ source_type = "all"
- def __init__(self, sysconfig=system_config):
+ def __init__(self, source_type = "all", sysconfig=system_config):
self.part_handlers = {
'text/x-shellscript' : self.handle_user_script,
'text/cloud-config' : self.handle_cloud_config,
@@ -182,6 +189,7 @@ class CloudInit:
}
self.sysconfig=sysconfig
self.cfg=self.read_cfg()
+ self.source_type = source_type
def read_cfg(self):
if self.cfg:
@@ -232,18 +240,21 @@ class CloudInit:
if self.datasource is not None: return True
if self.restore_from_cache():
+ log.debug("restored from cache type %s" % self.datasource)
return True
dslist=[ ]
cfglist=self.cfg['cloud_type']
if cfglist == "auto":
- dslist = self.auto_order
+ dslist = self.auto_orders[self.source_type]
elif cfglist:
for ds in cfglist.split(','):
dslist.append(strip(ds).tolower())
for ds in dslist:
- if ds not in self.datasource_map: continue
+ if ds not in self.datasource_map:
+ log.warn("data source %s not found in map" % ds)
+ continue
try:
s = self.datasource_map[ds]()
if s.get_data():
@@ -252,9 +263,11 @@ class CloudInit:
log.debug("found data source %s" % ds)
return True
except Exception as e:
+ log.warn("get_data of %s raised %s" % (ds,e))
+ util.logexc(log)
pass
- log.critical("Could not find data source")
- raise Exception("Could not find data source")
+ log.debug("did not find data source from %s" % dslist)
+ raise DataSourceNotFoundException("Could not find data source")
def get_userdata(self):
return(self.datasource.get_userdata())
@@ -475,3 +488,6 @@ def purge_cache():
except:
return(False)
return(True)
+
+class DataSourceNotFoundException(Exception):
+ pass
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 7f5c1db4..137921ed 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -21,6 +21,13 @@ import errno
import subprocess
from Cheetah.Template import Template
import cloudinit
+import urllib2
+import logging
+import traceback
+
+WARN = logging.WARN
+DEBUG = logging.DEBUG
+INFO = logging.INFO
def read_conf(fname):
try:
@@ -114,3 +121,29 @@ def render_to_file(template, outfile, searchList):
f.write(t.respond())
f.close()
+# raise OSError with enoent if not found
+def read_seeded(base="", ext=".raw", timeout=2):
+ if base.startswith("/"):
+ base="file://%s" % base
+
+ ud_url = "%s%s%s" % (base, "user-data", ext)
+ md_url = "%s%s%s" % (base, "meta-data", ext)
+
+ try:
+ md_resp = urllib2.urlopen(urllib2.Request(md_url), timeout=timeout)
+ ud_resp = urllib2.urlopen(urllib2.Request(ud_url), timeout=timeout)
+
+ md_str = md_resp.read()
+ ud = ud_resp.read()
+ md = yaml.load(md_str)
+
+ return(md,ud)
+ except urllib2.HTTPError:
+ raise
+ except urllib2.URLError, e:
+ if isinstance(e.reason,OSError) and e.reason.errno == errno.ENOENT:
+ raise e.reason
+ raise e
+
+def logexc(log,lvl=logging.DEBUG):
+ log.log(lvl,traceback.format_exc())
diff --git a/doc/examples/preseed-metadata.txt b/doc/examples/preseed-metadata.txt
new file mode 100644
index 00000000..62156826
--- /dev/null
+++ b/doc/examples/preseed-metadata.txt
@@ -0,0 +1,24 @@
+# this is yaml formated data
+# it is expected to be roughly what you would get from running the following
+# on an ec2 instance:
+# python -c 'import boto.utils, yaml; print(yaml.dump(boto.utils.get_instance_metadata()))'
+ami-id: ami-fd4aa494
+ami-launch-index: '0'
+ami-manifest-path: ubuntu-images-us/ubuntu-lucid-10.04-amd64-server-20100427.1.manifest.xml
+block-device-mapping: {ami: sda1, ephemeral0: sdb, ephemeral1: sdc, root: /dev/sda1}
+hostname: domU-12-31-38-07-19-44.compute-1.internal
+instance-action: none
+instance-id: i-87018aed
+instance-type: m1.large
+kernel-id: aki-c8b258a1
+local-hostname: domU-12-31-38-07-19-44.compute-1.internal
+local-ipv4: 10.223.26.178
+placement: {availability-zone: us-east-1d}
+public-hostname: ec2-184-72-174-120.compute-1.amazonaws.com
+public-ipv4: 184.72.174.120
+public-keys:
+ ec2-keypair.us-east-1: [ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCD9dlT00vOUC8Ttq6YH8RzUCVqPQl6HaSfWSTKYnZiVCpTBj1CaRZPLRLmkSB9Nziy4aRJa/LZMbBHXytQKnB1psvNknqC2UNlrXXMk+Vx5S4vg21MXYYimK4uZEY0Qz29QUiTyNsx18jpAaF4ocUpTpRhxPEBCcSCDmMbc27MU2XuTbasM2NjW/w0bBF3ZFhdH68dZICXdTxS2jUrtrCnc1D/QXVZ5kQO3jsmSyJg8E0nE+6Onpx2YRoVRSwjpGzVZ+BlXPnN5xBREBG8XxzhNFHJbek+RgK5TfL+k4yD4XhnVZuZu53cBAFhj+xPKhtisSd+YmaEq+Jt9uS0Ekd5
+ ec2-keypair.us-east-1, '']
+reservation-id: r-e2225889
+security-groups: default
+
diff --git a/setup.py b/setup.py
index 017a350e..6f4da2b7 100755
--- a/setup.py
+++ b/setup.py
@@ -38,6 +38,8 @@ setup(name='cloud-init',
('/etc/cloud/templates', glob('templates/*')),
('/etc/init', glob('upstart/*.conf')),
('/usr/share/cloud-init', []),
+ ('/usr/lib/cloud-init',
+ ['tools/uncloud-init','tools/write-mime-multipart']),
('/usr/share/doc/cloud-init', glob('doc/*.txt')),
('/usr/share/doc/cloud-init/examples', glob('doc/examples/*')),
],
diff --git a/tools/uncloud-init b/tools/uncloud-init
new file mode 100755
index 00000000..c0bc0b4f
--- /dev/null
+++ b/tools/uncloud-init
@@ -0,0 +1,138 @@
+#!/bin/sh
+# vi: ts=4 noexpandtab
+
+# This script is meant to "kvmify" an image. Its not meant to be
+# terribly robust, or a good idea to ever run in a "real image".
+# its' only intended method of invocation is from the kernel as 'init'
+# in which case it will then invoke /sbin/init after it is done
+# init=/path/to/kvmify-init
+
+KEY="xupdate"
+UMOUNT=""
+RMDIR=""
+MARK=/root/uncloud-init-ran
+ROOT_RW=""
+
+doexec() {
+ [ -z "$ROOT_RW" ] || date > "${MARK}";
+ cleanup;
+ log "invoking /sbin/init $*"
+ exec /sbin/init "$@";
+}
+log() { echo "::${0##*/}:" "$@"; }
+cleanup() {
+ [ -z "${UMOUNT}" ] || { umount "${UMOUNT}" && unset UMOUNT; }
+ [ -z "${RMDIR}" ] || { rm -Rf "${RMDIR}" && unset RMDIR; }
+ [ -z "${ROOT_RW}" ] || { mount -o remount,ro / ; unset ROOT_RW; }
+}
+
+updateFrom() {
+ local dev=$1 fmt=$2
+ local mp="";
+
+ [ "${fmt}" = "tar" -o "${fmt}" = "mnt" ] ||
+ { log FAIL "unknown format ${fmt}"; return 1; }
+
+ log INFO "updating from ${dev} format ${fmt}"
+ [ ! -e "${dev}" -a -e "/dev/${dev}" ] && dev="/dev/${dev}"
+ [ -e "${dev}" ] || { echo "no file $dev"; return 2; }
+
+ mp=$(mktemp -d "${TEMPDIR:-/tmp}/update.XXXXXX") &&
+ RMDIR="${mp}" ||
+ { log FAIL "failed to mktemp"; return 1; }
+
+ if [ "$fmt" = "tar" ]; then
+ dd "if=${dev}" | ( tar -C "${mp}" -xf - ) ||
+ { log FAIL "failed to extract ${dev}"; return 1; }
+ elif [ "$fmt" = "mnt" ]; then
+ mount -o ro "${dev}" "${mp}" && UMOUNT=${mp} ||
+ { log FAIL "failed mount ${mp}"; return 1; }
+ else
+ log FAIL "unknown format ${fmt}"; return 1;
+ fi
+
+ if [ -d "${mp}/updates" ]; then
+ rsync -av "${mp}/updates/" "/" ||
+ { log FAIL "failed rsync updates/ /"; return 1; }
+ fi
+ if [ -d "${mp}/updates.tar" ]; then
+ tar -C / -xvf "${mp}/updates.tar" ||
+ { log FAIL "failed tar -C / -xvf ${mp}/updates.tar"; return 1; }
+ fi
+ script="${mp}/updates.script"
+ if [ -f "${script}" -a -x "${script}" ]; then
+ MP_DIR=${mp} "${mp}/updates.script" ||
+ { log FAIL "failed to run updates.script"; return 1; }
+ fi
+}
+
+fail() { { [ $# -eq 0 ] && log "FAILING" ; } || log "$@"; exit 1; }
+
+[ -s "$MARK" ] && { log "already updated" ; doexec "$@"; }
+
+mount -o remount,rw / || fail "failed to mount rw"
+ROOT_RW=1
+
+if [ ! -e /proc/cmdline ]; then
+ mount -t proc /proc /proc
+ read cmdline < /proc/cmdline
+ umount /proc
+else
+ read cmdline < /proc/cmdline
+fi
+
+ubuntu_pass=""
+
+for x in ${cmdline}; do
+ case "$x" in
+ ${KEY}=*)
+ val=${x#${KEY}=}
+ dev=${val%:*}
+ [ "${dev}" = "${val}" ] && fmt="" || fmt=${val#${dev}:}
+ log "update from ${dev},${fmt}"
+ updateFrom "${dev}" "${fmt}" || fail "update failed"
+ log "end update ${dev},${fmt}"
+ ;;
+ ubuntu-pass=*|ubuntu_pass=*) ubuntu_pass=${x#*=};;
+ helpmount) helpmount=1;;
+ root=*) rootspec=${x#root=};;
+ esac
+done
+
+if [ "${ubuntu_pass}" = "R" -o "${ubuntu_pass}" = "random" ]; then
+ ubuntu_pass=$(python -c 'import string, random;
+random.seed(); print "".join(random.sample(string.letters+string.digits, 8))')
+ log "settting ubuntu pass = ${ubuntu_pass}"
+ printf "\n===\nubuntu_pass = %s\n===\n" "${ubuntu_pass}" >/dev/ttyS0
+fi
+
+[ -z "${ubuntu_pass}" ] ||
+ printf "ubuntu:%s\n" "${ubuntu_pass}" > /root/ubuntu-user-pass
+
+if [ -e /root/ubuntu-user-pass ]; then
+ log "changing ubuntu user's password!"
+ chpasswd < /root/ubuntu-user-pass ||
+ log "FAIL: failed changing pass"
+fi
+
+cp /etc/init/tty2.conf /etc/init/ttyS0.conf &&
+ sed -i s,tty2,ttyS0,g /etc/init/ttyS0.conf 2>/dev/null &&
+ log "enabled console on ttyS0"
+
+pa=PasswordAuthentication
+sed -i "s,${pa} no,${pa} yes," /etc/ssh/sshd_config 2>/dev/null &&
+ log "enabled passwd auth in ssh" ||
+ log "failed to enable passwd ssh"
+
+grep -q vga16fb /etc/modprobe.d/blacklist.conf || {
+ echo "blacklist vga16fb" >> /etc/modprobe.d/blacklist.conf &&
+ log "blacklisted vga16fb"
+}
+
+#lstr="${rootspec}"
+#if ! grep -q "^${lstr}[[:space:]]" /etc/fstab; then
+# log "changing / in /etc/ftab to agree with cmdline (${lstr}) (bug 509841)"
+# sed -i "s,^\([^[[:space:]#]\+\)\([[:space:]]\+\)/\([[:space:]]\+\),${lstr}\2/\3," /etc/fstab
+#fi
+
+doexec "$@"
diff --git a/upstart/cloud-init-local.conf b/upstart/cloud-init-local.conf
new file mode 100644
index 00000000..b6eb21b4
--- /dev/null
+++ b/upstart/cloud-init-local.conf
@@ -0,0 +1,9 @@
+# cloud-init - the initial cloud-init job
+# crawls metadata service, emits cloud-config
+start on mounted MOUNTPOINT=/
+
+task
+
+console output
+
+exec /usr/bin/cloud-init start-local
diff --git a/upstart/cloud-init.conf b/upstart/cloud-init.conf
index 2a066105..cb2b437b 100644
--- a/upstart/cloud-init.conf
+++ b/upstart/cloud-init.conf
@@ -1,6 +1,7 @@
# cloud-init - the initial cloud-init job
# crawls metadata service, emits cloud-config
-start on (mounted MOUNTPOINT=/ and net-device-up IFACE=eth0)
+start on (mounted MOUNTPOINT=/ and net-device-up IFACE=eth0 and \
+ stopped cloud-init-local )
task