diff options
-rw-r--r-- | ChangeLog | 21 | ||||
-rwxr-xr-x | cloud-init.py | 13 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_byobu.py | 43 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_grub_dpkg.py | 3 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_mounts.py | 9 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_set_hostname.py | 7 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_ssh.py | 42 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_timezone.py | 1 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_update_etc_hosts.py | 34 | ||||
-rw-r--r-- | cloudinit/DataSourceEc2.py | 87 | ||||
-rw-r--r-- | cloudinit/UserDataHandler.py | 1 | ||||
-rw-r--r-- | cloudinit/__init__.py | 27 | ||||
-rw-r--r-- | cloudinit/boto_utils.py | 8 | ||||
-rw-r--r-- | cloudinit/util.py | 6 | ||||
-rw-r--r-- | config/cloud.cfg | 2 | ||||
-rw-r--r-- | doc/examples/cloud-config-datasources.txt | 7 | ||||
-rw-r--r-- | doc/examples/cloud-config.txt | 39 | ||||
-rw-r--r-- | doc/examples/seed/README | 4 | ||||
-rwxr-xr-x | tools/write-mime-multipart.py | 51 | ||||
-rw-r--r-- | upstart/cloud-init-nonet.conf | 2 |
20 files changed, 267 insertions, 140 deletions
@@ -5,6 +5,27 @@ (LP: #739694) - fix bug in resizefs cloud-config that would cause trace based on failure of 'blkid /dev/root' (LP: #726938) + - convert dos formated files to unix for user-scripts, boothooks, + and upstart jobs (LP: #744965) + - fix bug in seeding of grub dpkg configuration (LP: #752361) due + to renamed devices in newer (natty) kernels (/dev/sda1 -> /dev/xvda1) + - make metadata urls configurable, to support eucalyptus in + STATIC or SYSTEM modes (LP: #761847) + - support disabling byobu in cloud-config + - run cc_ssh as a cloud-init module so it is guaranteed to run before + ssh starts (LP: #781101) + - make prefix for keys added to /root/.ssh/authorized_keys configurable + and add 'no-port-forwarding,no-agent-forwarding,no-X11-forwarding' + to the default (LP: #798505) + - make 'cloud-config ready' command configurable (LP: #785551) + - make fstab fields used to 'fill in' shorthand entries configurable + This means you do not have to have 'nobootwait' in the values + (LP: #785542) + - read /etc/ssh/sshd_config for AuthorizedKeysFile rather than + assuming ~/.ssh/authorized_keys (LP: #731849) + - fix cloud-init in ubuntu lxc containers (LP: #800824) + - sanitize hosts file for system's hostname to 127.0.1.1 (LP: #802637) + - add chef support (cloudinit/CloudConfig/cc_chef.py) 0.6.1: - fix bug in fixing permission on /var/log/cloud-init.log (LP: #704509) - improve comment strings in rsyslog file tools/21-cloudinit.conf diff --git a/cloud-init.py b/cloud-init.py index ee08c191..09c537f1 100755 --- a/cloud-init.py +++ b/cloud-init.py @@ -146,9 +146,6 @@ def main(): warn("consuming user data failed!\n") raise - # finish, send the cloud-config event - cloud.initctl_emit() - cfg_path = cloudinit.get_ipath_cur("cloud_config") cc = CC.CloudConfig(cfg_path, cloud) @@ -163,6 +160,16 @@ def main(): except Exception as e: warn("Failed to get and set output config: %s\n" % e) + # send the cloud-config ready event + cc_path = cloudinit.get_ipath_cur('cloud_config') + cc_ready = cc.cfg.get("cc_ready_cmd", + ['initctl', 'emit', 'cloud-config', + '%s=%s' % (cloudinit.cfg_env_name, cc_path) ]) + if cc_ready: + if isinstance(cc_ready,str): + cc_ready = [ 'sh', '-c', cc_ready] + subprocess.Popen(cc_ready).communicate() + module_list = CC.read_cc_modules(cc.cfg,"cloud_init_modules") failures = [] diff --git a/cloudinit/CloudConfig/cc_byobu.py b/cloudinit/CloudConfig/cc_byobu.py index 1a4545af..406a1f67 100644 --- a/cloudinit/CloudConfig/cc_byobu.py +++ b/cloudinit/CloudConfig/cc_byobu.py @@ -27,19 +27,40 @@ def handle(name,cfg,cloud,log,args): if not value: return - if value == "user": - user = util.get_cfg_option_str(cfg,"user","ubuntu") - cmd = [ 'sudo', '-Hu', user, 'byobu-launcher-install' ] - elif value == "system": - shcmd="echo '%s' | debconf-set-selections && %s" % \ - ( "byobu byobu/launch-by-default boolean true", - "dpkg-reconfigure byobu --frontend=noninteractive" ) - cmd = [ "/bin/sh", "-c", shcmd ] - else: + if value == "user" or value == "system": + value = "enable-%s" % value + + valid = ( "enable-user", "enable-system", "enable", + "disable-user", "disable-system", "disable" ) + if not value in valid: log.warn("Unknown value %s for byobu_by_default" % value) - return - log.debug("enabling byobu for %s" % value) + mod_user = value.endswith("-user") + mod_sys = value.endswith("-system") + if value.startswith("enable"): + bl_inst = "install" + dc_val = "byobu byobu/launch-by-default boolean true" + mod_sys = True + else: + if value == "disable": + mod_user = True + mod_sys = True + bl_inst = "uninstall" + dc_val = "byobu byobu/launch-by-default boolean false" + + shcmd = "" + if mod_user: + user = util.get_cfg_option_str(cfg,"user","ubuntu") + shcmd += " sudo -Hu \"%s\" byobu-launcher-%s" % (user, bl_inst) + shcmd += " || X=$(($X+1)); " + if mod_sys: + shcmd += "echo \"%s\" | debconf-set-selections" % dc_val + shcmd += " && dpkg-reconfigure byobu --frontend=noninteractive" + shcmd += " || X=$(($X+1)); " + + cmd = [ "/bin/sh", "-c", "%s %s %s" % ("X=0;", shcmd, "exit $X" ) ] + + log.debug("setting byobu to %s" % value) try: subprocess.check_call(cmd) diff --git a/cloudinit/CloudConfig/cc_grub_dpkg.py b/cloudinit/CloudConfig/cc_grub_dpkg.py index dafb43cf..b26e90e8 100644 --- a/cloudinit/CloudConfig/cc_grub_dpkg.py +++ b/cloudinit/CloudConfig/cc_grub_dpkg.py @@ -31,7 +31,8 @@ def handle(name,cfg,cloud,log,args): idevs_empty=util.get_cfg_option_str(cfg["grub-dpkg"], "grub-pc/install_devices_empty",None) - if os.path.exists("/dev/sda1") and not os.path.exists("/dev/sda"): + if (( os.path.exists("/dev/sda1") and not os.path.exists("/dev/sda") ) or + ( os.path.exists("/dev/xvda1") and not os.path.exists("/dev/xvda") )): if idevs == None: idevs="" if idevs_empty == None: idevs_empty="true" else: diff --git a/cloudinit/CloudConfig/cc_mounts.py b/cloudinit/CloudConfig/cc_mounts.py index 8ee4f718..592a030a 100644 --- a/cloudinit/CloudConfig/cc_mounts.py +++ b/cloudinit/CloudConfig/cc_mounts.py @@ -32,12 +32,13 @@ def is_mdname(name): return False def handle(name,cfg,cloud,log,args): - # these are our default set of mounts - defmnts = [ [ "ephemeral0", "/mnt", "auto", "defaults,nobootwait", "0", "2" ], - [ "swap", "none", "swap", "sw", "0", "0" ] ] - # fs_spec, fs_file, fs_vfstype, fs_mntops, fs-freq, fs_passno defvals = [ None, None, "auto", "defaults,nobootwait", "0", "2" ] + defvals = cfg.get("mount_default_fields", defvals) + + # these are our default set of mounts + defmnts = [ [ "ephemeral0", "/mnt", "auto", defvals[3], "0", "2" ], + [ "swap", "none", "swap", "sw", "0", "0" ] ] cfgmnt = [ ] if cfg.has_key("mounts"): diff --git a/cloudinit/CloudConfig/cc_set_hostname.py b/cloudinit/CloudConfig/cc_set_hostname.py index 49368019..2b130810 100644 --- a/cloudinit/CloudConfig/cc_set_hostname.py +++ b/cloudinit/CloudConfig/cc_set_hostname.py @@ -24,12 +24,7 @@ def handle(name,cfg,cloud,log,args): return(True) try: - hostname_prefix = util.get_cfg_option_str(cfg, "hostname_prefix", None) - hostname_attr = util.get_cfg_option_str(cfg, "hostname_attribute", "hostname") - hostname_function = getattr(cloud, 'get_' + hostname_attr, None) - if hostname_fucntion is None: hostname_fucntion = cloud.get_hostname - hostname = util.get_cfg_option_str(cfg,"hostname", hostname_function) - if hostname_prefix: hostname = hostname_prefix + "-" + hostname + hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname()) set_hostname(hostname, log) except Exception as e: util.logexc(log) diff --git a/cloudinit/CloudConfig/cc_ssh.py b/cloudinit/CloudConfig/cc_ssh.py index c4603d2b..ee03de22 100644 --- a/cloudinit/CloudConfig/cc_ssh.py +++ b/cloudinit/CloudConfig/cc_ssh.py @@ -20,7 +20,15 @@ import os import glob import subprocess +DISABLE_ROOT_OPTS="no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\" rather than the user \\\"root\\\".\';echo;sleep 10\"" + + +global_log = None + def handle(name,cfg,cloud,log,args): + global global_log + global_log = log + # remove the static keys from the pristine image for f in glob.glob("/etc/ssh/ssh_host_*_key*"): try: os.unlink(f) @@ -55,14 +63,17 @@ def handle(name,cfg,cloud,log,args): try: user = util.get_cfg_option_str(cfg,'user') disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) + disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", + DISABLE_ROOT_OPTS) keys = cloud.get_public_ssh_keys() if cfg.has_key("ssh_authorized_keys"): cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) - apply_credentials(keys,user,disable_root) + apply_credentials(keys,user,disable_root, disable_root_opts) except: + util.logexc(log) log.warn("applying credentials failed!\n") send_ssh_keys_to_console() @@ -70,13 +81,13 @@ def handle(name,cfg,cloud,log,args): def send_ssh_keys_to_console(): subprocess.call(('/usr/lib/cloud-init/write-ssh-key-fingerprints',)) -def apply_credentials(keys, user, disable_root): +def apply_credentials(keys, user, disable_root, disable_root_opts=DISABLE_ROOT_OPTS): keys = set(keys) if user: setup_user_keys(keys, user, '') if disable_root: - key_prefix = 'command="echo \'Please login as the user \\\"%s\\\" rather than the user \\\"root\\\".\';echo;sleep 10" ' % user + key_prefix = disable_root_opts.replace('$USER', user) else: key_prefix = '' @@ -93,13 +104,34 @@ def setup_user_keys(keys, user, key_prefix): os.mkdir(ssh_dir) os.chown(ssh_dir, pwent.pw_uid, pwent.pw_gid) - authorized_keys = '%s/.ssh/authorized_keys' % pwent.pw_dir + try: + ssh_cfg = parse_ssh_config() + akeys = ssh_cfg.get("AuthorizedKeysFile","%h/.ssh/authorized_keys") + akeys = akeys.replace("%h", pwent.pw_dir) + akeys = akeys.replace("%u", user) + authorized_keys = akeys + except Exception as e: + authorized_keys = '%s/.ssh/authorized_keys' % pwent.pw_dir + util.logexc(global_log) + fp = open(authorized_keys, 'a') - fp.write(''.join(['%s%s\n' % (key_prefix, key) for key in keys])) + key_prefix = key_prefix.replace("\n"," ") + fp.write(''.join(['%s %s\n' % (key_prefix.strip(), key) for key in keys])) fp.close() os.chown(authorized_keys, pwent.pw_uid, pwent.pw_gid) os.umask(saved_umask) +def parse_ssh_config(fname="/etc/ssh/sshd_config"): + ret = { } + fp=open(fname) + for l in fp.readlines(): + l = l.strip() + if not l or l.startswith("#"): + continue + key,val = l.split(None,1) + ret[key]=val + fp.close() + return(ret) diff --git a/cloudinit/CloudConfig/cc_timezone.py b/cloudinit/CloudConfig/cc_timezone.py index f221819e..a26df8f9 100644 --- a/cloudinit/CloudConfig/cc_timezone.py +++ b/cloudinit/CloudConfig/cc_timezone.py @@ -25,7 +25,6 @@ frequency = per_instance tz_base = "/usr/share/zoneinfo" def handle(name,cfg,cloud,log,args): - print args if len(args) != 0: timezone = args[0] else: diff --git a/cloudinit/CloudConfig/cc_update_etc_hosts.py b/cloudinit/CloudConfig/cc_update_etc_hosts.py index 856cbae1..10ee5435 100644 --- a/cloudinit/CloudConfig/cc_update_etc_hosts.py +++ b/cloudinit/CloudConfig/cc_update_etc_hosts.py @@ -17,12 +17,44 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import cloudinit.util as util from cloudinit.CloudConfig import per_always +import platform +import StringIO frequency = per_always def handle(name,cfg,cloud,log,args): if not util.get_cfg_option_bool(cfg,"manage_etc_hosts",False): - log.debug("manage_etc_hosts is not set. not modifying /etc/hosts") + log.debug("manage_etc_hosts is not set, checking sanity of /etc/hosts") + with open('/etc/hosts', 'r') as etchosts: + current_hostname = platform.node() + hosts_line = "# Added by cloud-init\n127.0.1.1\t%s.localdomain %s\n" % (current_hostname, current_hostname) + need_write = False + need_change = True + new_etchosts = StringIO.StringIO() + for line in etchosts: + split_line = [s.strip() for s in line.split()] + # skip over malformed /etc/hosts entries + if len(split_line) < 2: + continue + ip, hostnames = split_line[0], split_line[1:] + if ip == "127.0.1.1": + for hostname in hostnames: + if hostname == current_hostname: + need_change = False + if need_change == True: + line = hosts_line + need_change = False + need_write = True + new_etchosts.write(line) + etchosts.close() + if need_change == True: + new_etchosts.write(hosts_line) + need_write = True + if need_write == True: + new_etcfile = open ('/etc/hosts','wb') + new_etcfile.write(new_etchosts.getvalue()) + new_etcfile.close() + new_etchosts.close() return try: diff --git a/cloudinit/DataSourceEc2.py b/cloudinit/DataSourceEc2.py index 9f1cf840..4abb6e47 100644 --- a/cloudinit/DataSourceEc2.py +++ b/cloudinit/DataSourceEc2.py @@ -27,10 +27,12 @@ import sys import boto_utils import os.path import errno +import urlparse class DataSourceEc2(DataSource.DataSource): api_ver = '2009-04-04' seeddir = seeddir + '/ec2' + metadata_address = "http://169.254.169.254" def __str__(self): return("DataSourceEc2") @@ -46,8 +48,8 @@ class DataSourceEc2(DataSource.DataSource): try: if not self.wait_for_metadata_service(): return False - self.userdata_raw = boto_utils.get_instance_userdata(self.api_ver) - self.metadata = boto_utils.get_instance_metadata(self.api_ver) + self.userdata_raw = boto_utils.get_instance_userdata(self.api_ver, None, self.metadata_address) + self.metadata = boto_utils.get_instance_metadata(self.api_ver, self.metadata_address) return True except Exception as e: print e @@ -100,30 +102,58 @@ class DataSourceEc2(DataSource.DataSource): log.warn("Failed to get timeout, using %s" % timeout) sleeptime = 1 - address = '169.254.169.254' + + def_mdurls = ["http://169.254.169.254", "http://instance-data:8773"] + try: + mdurls = mcfg.get("metadata_urls", def_mdurls) + except Exception as e: + mdurls = def_mdurls + util.logexc(log) + log.warn("Failed to get metadata URLs, using defaults") + starttime = time.time() - - url="http://%s/%s/meta-data/instance-id" % (address,self.api_ver) - for x in range(sleeps): - # given 100 sleeps, this ends up total sleep time of 1050 sec - sleeptime=int(x/5)+1 - reason = "" - try: - req = urllib2.Request(url) - resp = urllib2.urlopen(req, timeout=timeout) - if resp.read() != "": return True - reason = "empty data [%s]" % resp.getcode() - except urllib2.HTTPError as e: - reason = "http error [%s]" % e.code - except urllib2.URLError as e: - reason = "url error [%s]" % e.reason - - if x == 0: - log.warning("waiting for metadata service at %s\n" % url) - - log.warning(" %s [%02s/%s]: %s\n" % - (time.strftime("%H:%M:%S",time.gmtime()), x+1, sleeps, reason)) + # Remove addresses from the list that wont resolve. + filtered = [x for x in mdurls if try_to_resolve_metadata(x)] + + if set(filtered) != set(mdurls): + log.debug("removed the following from metadata urls: %s" % + list((set(mdurls) - set(filtered)))) + + if len(filtered): + mdurls = filtered + else: + log.warn("Empty metadata url list! using default list") + mdurls = def_mdurls + + log.debug("Searching the following metadata urls: %s" % mdurls) + + for x in range(sleeps): + for url in mdurls: + iurl="%s/%s/meta-data/instance-id" % (url, self.api_ver) + + # given 100 sleeps, this ends up total sleep time of 1050 sec + sleeptime=int(x/5)+1 + + reason = "" + try: + req = urllib2.Request(iurl) + resp = urllib2.urlopen(req, timeout=timeout) + if resp.read() != "": + self.metadata_address = url + log.debug("Using metadata source: '%s'" % url) + return True + reason = "empty data [%s]" % resp.getcode() + except urllib2.HTTPError as e: + reason = "http error [%s]" % e.code + except urllib2.URLError as e: + reason = "url error [%s]" % e.reason + + #not needed? Addresses being checked are displayed above + #if x == 0: + # log.warn("waiting for metadata service at %s" % url) + + log.warn("'%s' failed: %s" % (url, reason)) time.sleep(sleeptime) log.critical("giving up on md after %i seconds\n" % @@ -181,6 +211,15 @@ class DataSourceEc2(DataSource.DataSource): return True return False +def try_to_resolve_metadata(url): + try: + addr = urlparse.urlsplit(url).netloc.split(":")[0] + socket.getaddrinfo(addr, None) + return True + except Exception as e: + return False + + datasources = [ ( DataSourceEc2, ( DataSource.DEP_FILESYSTEM , DataSource.DEP_NETWORK ) ), ] diff --git a/cloudinit/UserDataHandler.py b/cloudinit/UserDataHandler.py index fbb000fc..83377dab 100644 --- a/cloudinit/UserDataHandler.py +++ b/cloudinit/UserDataHandler.py @@ -74,7 +74,6 @@ def explode_cc_archive(archive,parts): if mtype == None: mtype = type_from_startswith(payload,def_type) - print "adding %s,%s" % (filename, mtype) parts['content'].append(content) parts['names'].append(filename) parts['types'].append(mtype) diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py index 24e12d08..82dfaa8f 100644 --- a/cloudinit/__init__.py +++ b/cloudinit/__init__.py @@ -243,11 +243,6 @@ class CloudInit: util.write_file(self.get_ipath('userdata'), self.datasource.get_userdata(), 0600) - def initctl_emit(self): - cc_path = get_ipath_cur('cloud_config') - subprocess.Popen(['initctl', 'emit', 'cloud-config', - '%s=%s' % (cfg_env_name,cc_path)]).communicate() - def sem_getpath(self,name,freq): if freq == 'once-per-instance': return("%s/%s" % (self.get_ipath("sem"),name)) @@ -393,14 +388,15 @@ class CloudInit: filename=filename.replace(os.sep,'_') scriptsdir = get_ipath_cur('scripts') util.write_file("%s/%s" % - (scriptsdir,filename), payload, 0700) + (scriptsdir,filename), util.dos2unix(payload), 0700) def handle_upstart_job(self,data,ctype,filename,payload): if ctype == "__end__" or ctype == "__begin__": return if not filename.endswith(".conf"): filename=filename+".conf" - util.write_file("%s/%s" % ("/etc/init",filename), payload, 0644) + util.write_file("%s/%s" % ("/etc/init",filename), + util.dos2unix(payload), 0644) def handle_cloud_config(self,data,ctype,filename,payload): if ctype == "__begin__": @@ -427,26 +423,15 @@ class CloudInit: if ctype == "__begin__": return filename=filename.replace(os.sep,'_') + payload = util.dos2unix(payload) prefix="#cloud-boothook" - dos=False start = 0 if payload.startswith(prefix): - start = len(prefix) - if payload[start] == '\r': - start=start+1 - dos = True - else: - if payload.find('\r\n',0,100) >= 0: - dos = True - - if dos: - payload=payload[start:].replace('\r\n','\n') - elif start != 0: - payload=payload[start:] + start = len(prefix) + 1 boothooks_dir = self.get_ipath("boothooks") filepath = "%s/%s" % (boothooks_dir,filename) - util.write_file(filepath, payload, 0700) + util.write_file(filepath, payload[start:], 0700) try: env=os.environ.copy() env['INSTANCE_ID']= self.datasource.get_instance_id() diff --git a/cloudinit/boto_utils.py b/cloudinit/boto_utils.py index b38483fa..a2cb9ca6 100644 --- a/cloudinit/boto_utils.py +++ b/cloudinit/boto_utils.py @@ -60,7 +60,7 @@ def retry_url(url, retry_on_404=True): sys.stderr.write('Caught exception reading instance data, giving up\n') return '' -def get_instance_metadata(version='latest'): +def get_instance_metadata(version='latest',url='http://169.254.169.254'): """ Returns the instance metadata as a nested Python dictionary. Simple values (e.g. local_hostname, hostname, etc.) will be @@ -68,11 +68,11 @@ def get_instance_metadata(version='latest'): be stored in the dict as a list of string values. More complex fields such as public-keys and will be stored as nested dicts. """ - url = 'http://169.254.169.254/%s/meta-data/' % version + url = '%s/%s/meta-data/' % (url,version) return _get_instance_metadata(url) -def get_instance_userdata(version='latest', sep=None): - url = 'http://169.254.169.254/%s/user-data' % version +def get_instance_userdata(version='latest', sep=None,url='http://169.254.169.254'): + url = '%s/%s/user-data' % (url,version) user_data = retry_url(url, retry_on_404=False) if user_data: if sep: diff --git a/cloudinit/util.py b/cloudinit/util.py index fc4233de..8f6a6b0d 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -389,3 +389,9 @@ def shellify(cmdlist): else: content="%s%s\n" % ( content, str(args) ) return content + +def dos2unix(input): + # find first end of line + pos = input.find('\n') + if pos <= 0 or input[pos-1] != '\r': return(input) + return(input.replace('\r\n','\n')) diff --git a/config/cloud.cfg b/config/cloud.cfg index 3aaa3eda..b593d90c 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -10,12 +10,12 @@ cloud_init_modules: - update_hostname - update_etc_hosts - rsyslog + - ssh cloud_config_modules: - mounts - ssh-import-id - locale - - ssh - set-passwords - grub-dpkg - apt-update-upgrade diff --git a/doc/examples/cloud-config-datasources.txt b/doc/examples/cloud-config-datasources.txt index 3333792e..e04f8976 100644 --- a/doc/examples/cloud-config-datasources.txt +++ b/doc/examples/cloud-config-datasources.txt @@ -8,3 +8,10 @@ datasource: # after each try, a sleep of int(try_number/5)+1 is done # default sleep is 30 retries : 30 + + #metadata_url: a list of URLs to check for metadata services + metadata_urls: + - http://169.254.169.254:80 + - http://instance-data:8773 + + diff --git a/doc/examples/cloud-config.txt b/doc/examples/cloud-config.txt index c1d0b278..edf58067 100644 --- a/doc/examples/cloud-config.txt +++ b/doc/examples/cloud-config.txt @@ -111,7 +111,7 @@ packages: # written to /etc/fstab. # - '/dev' can be ommitted for device names that begin with: xvd, sd, hd, vd # - if an entry does not have all 6 fields, they will be filled in -# from the following: [ None, None, "auto", "defaults,nobootwait", "0", "2" ] +# with values from 'mount_default_fields' below. # # Note, that you should set 'nobootwait' (see man fstab) for volumes that may # not be attached at instance boot (or reboot) @@ -122,6 +122,11 @@ mounts: - [ xvdh, /opt/data, "auto", "defaults,nobootwait", "0", "0" ] - [ dd, /dev/zero ] +# mount_default_fields +# These values are used to fill in any entries in 'mounts' that are not +# complete. This must be an array, and must have 7 fields. +mount_default_fields: [ None, None, "auto", "defaults,nobootwait", "0", "2" ] + # add each entry to ~/.ssh/authorized_keys for the configured user ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEA3FSyQwBI6Z+nCSjUUk8EEAnnkhXlukKoUPND/RRClWz2s5TCzIkd3Ou5+Cyz71X0XmazM3l5WgeErvtIwQMyT1KjNoMhoJMrJnWqQPOt5Q8zWd9qG7PBl9+eiH5qV7NZ mykey@host @@ -255,9 +260,14 @@ debconf_selections: | # Need to perserve newlines debconf debconf/frontend seen false # manage byobu defaults -# byobu_by_default: ('user'|'system') -# 'user' will set byobu 'launch-by-default' for the default user -# 'system' will enable launch-by-default for for all users +# byobu_by_default: +# 'user' or 'enable-user': set byobu 'launch-by-default' for the default user +# 'system' or 'enable-system' or 'enable': +# enable 'launch-by-default' for all users, do not modify default user +# 'disable': disable both default user and system +# 'disable-system': disable system +# 'disable-user': disable for default user +# not-set: no changes made byobu_by_default: system # disable ssh access as root. @@ -266,6 +276,15 @@ byobu_by_default: system # default: true disable_root: false +# disable_root_opts: the value of this variable will prefix the +# respective key in /root/.ssh/authorized_keys if disable_root is true +# see 'man authorized_keys' for more information on what you can do here +# +# The string '$USER' will be replaced with the username of the default user +# +# disable_root_opts: no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"$USER\" rather than the user \"root\".';echo;sleep 10" + + # set the locale to a given locale # default: en_US.UTF-8 locale: en_US.UTF-8 @@ -408,3 +427,15 @@ manual_cache_clean: False # on a per-always basis (to account for ebs stop/start), then set # manage_etc_hosts to True. The default is 'False' manage_etc_hosts: False + +# When cloud-init is finished running including having run +# cloud_init_modules, then it will run this command. The default +# is to emit an upstart signal as shown below. If the value is a +# list, it will be passed to Popen. If it is a string, it will be +# invoked through 'sh -c'. +# +# default value: +# cc_ready_cmd: [ initctl, emit, cloud-config, CLOUD_CFG=/var/lib/instance//cloud-config.txt ] +# example: +# cc_ready_cmd: [ sh, -c, 'echo HI MOM > /tmp/file' ] + diff --git a/doc/examples/seed/README b/doc/examples/seed/README index 927768f8..cc15839e 100644 --- a/doc/examples/seed/README +++ b/doc/examples/seed/README @@ -2,9 +2,9 @@ This directory is an example of a 'seed' directory. copying these files inside an instance's - /var/lib/cloud/data/cache/nocloud + /var/lib/cloud/seed/nocloud or - /var/lib/cloud/data/cache/nocloud-net + /var/lib/cloud/seed/nocloud-net will cause the 'DataSourceNoCloud' and 'DataSourceNoCloudNet' modules to enable and read the given data. diff --git a/tools/write-mime-multipart.py b/tools/write-mime-multipart.py deleted file mode 100755 index 0a67d4c5..00000000 --- a/tools/write-mime-multipart.py +++ /dev/null @@ -1,51 +0,0 @@ -#! /usr/bin/env python - -import sys, os -import email -import mimetypes -import re - -mimetypes.types_map['.sh'] = 'text/x-shellscript' -cloud_config_mark_strings = { '#!': 'text/x-shellscript', '#include': 'text/x-include-url', - '#cloud-config': 'text/cloud-config', '#upstart-job': 'text/upstart-job', - '#cloud-boothook': 'text/cloud-boothook' - } -def write_mime_multipart(): - multipart_msg = email.mime.Multipart.MIMEMultipart() - for arg in sys.argv[1:]: - if ',' in arg: - (msg_file, msg_type) = arg.split(',') - else: - msg_file = arg - msg_type = None - - msg_file = os.path.expanduser(msg_file) - if not os.path.isfile(msg_file): - print >> sys.stderr, "Can't find file %s" % arg - exit(1) - - if not msg_type: msg_type = get_type_from_file(arg) - msg = email.mime.base.MIMEBase(*msg_type.split('/')) - msg.set_payload(open(msg_file, 'r').read()) - multipart_msg.attach(msg) - - print multipart_msg.as_string() - -def get_type_from_file(filename): - first_line = open(filename).readline() - m = re.match('Content-Type: (\w+/\w+)', first_line) - if m: - return m.groups[1] - else: - for mark_string, mime_type in cloud_config_mark_strings.items(): - if first_line.startswith(mark_string): - return mime_type - return mimetypes.guess_type(filename)[0] or 'text/plain' - -if __name__ == '__main__': - if len(sys.argv) == 1 or '-h' in sys.argv or '--help' in sys.argv: - print "Usage: %s file1,application/cloud-config file2.sh ..." % os.path.basename(sys.argv[0]) - print "MIME Multipart message will be written to STDOUT" - exit(0) - write_mime_multipart() - diff --git a/upstart/cloud-init-nonet.conf b/upstart/cloud-init-nonet.conf index 12f21320..60f83a32 100644 --- a/upstart/cloud-init-nonet.conf +++ b/upstart/cloud-init-nonet.conf @@ -14,6 +14,8 @@ script [ -f /var/lib/cloud/instance/obj.pkl ] && exit 0 + start networking + short=10; long=60; sleep ${short} echo $UPSTART_JOB "waiting ${long} seconds for a network device." |