diff options
Diffstat (limited to 'ec2init/CloudConfig.py')
-rw-r--r-- | ec2init/CloudConfig.py | 407 |
1 files changed, 0 insertions, 407 deletions
diff --git a/ec2init/CloudConfig.py b/ec2init/CloudConfig.py deleted file mode 100644 index d1699f09..00000000 --- a/ec2init/CloudConfig.py +++ /dev/null @@ -1,407 +0,0 @@ -# -# Common code for the EC2 configuration files in Ubuntu -# Copyright (C) 2008-2010 Canonical Ltd. -# -# Author: Chuck Short <chuck.short@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 yaml -import re -import ec2init -import ec2init.util as util -import subprocess -import os -import glob -import sys - -per_instance="once-per-instance" - -class CloudConfig(): - cfgfile = None - handlers = { } - cfg = None - - def __init__(self,cfgfile): - self.cloud = ec2init.EC2Init() - self.cfg = self.get_config_obj(cfgfile) - self.cloud.get_data_source() - self.add_handler('apt-update-upgrade', self.h_apt_update_upgrade) - self.add_handler('config-ssh') - self.add_handler('disable-ec2-metadata', - self.h_disable_ec2_metadata, "always") - self.add_handler('config-mounts') - - def get_config_obj(self,cfgfile): - f=file(cfgfile) - cfg=yaml.load(f.read()) - f.close() - if cfg is None: cfg = { } - return(util.mergedict(cfg,self.cloud.cfg)) - - def convert_old_config(self): - # support reading the old ConfigObj format file and turning it - # into a yaml string - try: - f = file(self.conffile) - str=file.read().replace('=',': ') - f.close() - return str - except: - return("") - - def add_handler(self, name, handler=None, freq=None): - if handler is None: - try: - handler=getattr(self,'h_%s' % name.replace('-','_')) - except: - raise Exception("Unknown hander for name %s" %name) - if freq is None: - freq = per_instance - - self.handlers[name]= { 'handler': handler, 'freq': freq } - - def get_handler_info(self, name): - return(self.handlers[name]['handler'], self.handlers[name]['freq']) - - def parse_ssh_keys(self): - disableRoot = self.cfg['disable_root'] - if disableRoot == 'true': - value = 'disabled_root' - return value - else: - ec2Key = self.cfg['ec2_fetch_key'] - if ec2Key != 'none': - value = 'default_key' - return value - else: - return ec2Key - - def handle(self, name, args): - handler = None - freq = None - try: - (handler, freq) = self.get_handler_info(name) - except: - raise Exception("Unknown config key %s\n" % name) - - self.cloud.sem_and_run(name, freq, handler, [ name, args ]) - - def h_apt_update_upgrade(self,name,args): - update = util.get_cfg_option_bool(self.cfg, 'apt_update', False) - upgrade = util.get_cfg_option_bool(self.cfg, 'apt_upgrade', False) - - if not util.get_cfg_option_bool(self.cfg, \ - 'apt_preserve_sources_list', False): - if self.cfg.has_key("apt_mirror"): - mirror = self.cfg["apt_mirror"] - else: - mirror = self.cloud.get_mirror() - generate_sources_list(mirror) - - # process 'apt_sources' - if self.cfg.has_key('apt_sources'): - errors = add_sources(self.cfg['apt_sources']) - for e in errors: - warn("Source Error: %s\n" % ':'.join(e)) - - pkglist = [] - if 'packages' in self.cfg: - if isinstance(self.cfg['packages'],list): - pkglist = self.cfg['packages'] - else: pkglist.append(self.cfg['packages']) - - if update or upgrade or pkglist: - #retcode = subprocess.call(list) - subprocess.Popen(['apt-get', 'update']).communicate() - - e=os.environ.copy() - e['DEBIAN_FRONTEND']='noninteractive' - - if upgrade: - subprocess.Popen(['apt-get', 'upgrade', '--assume-yes'], env=e).communicate() - - if pkglist: - cmd=['apt-get', 'install', '--assume-yes'] - cmd.extend(pkglist) - subprocess.Popen(cmd, env=e).communicate() - - return(True) - - def h_disable_ec2_metadata(self,name,args): - if util.get_cfg_option_bool(self.cfg, "disable_ec2_metadata", False): - fwall="route add -host 169.254.169.254 reject" - subprocess.call(fwall.split(' ')) - - def h_config_ssh(self,name,args): - # remove the static keys from the pristine image - for f in glob.glob("/etc/ssh/ssh_host_*_key*"): - try: os.unlink(f) - except: pass - - if self.cfg.has_key("ssh_keys"): - # if there are keys in cloud-config, use them - key2file = { - "rsa_private" : ("/etc/ssh/ssh_host_rsa_key", 0600), - "rsa_public" : ("/etc/ssh/ssh_host_rsa_key.pub", 0644), - "dsa_private" : ("/etc/ssh/ssh_host_dsa_key", 0600), - "dsa_public" : ("/etc/ssh/ssh_host_dsa_key.pub", 0644) - } - - for key,val in self.cfg["ssh_keys"].items(): - if key2file.has_key(key): - util.write_file(key2file[key][0],val,key2file[key][1]) - else: - # if not, generate them - genkeys ='ssh-keygen -f /etc/ssh/ssh_host_rsa_key -t rsa -N ""; ' - genkeys+='ssh-keygen -f /etc/ssh/ssh_host_dsa_key -t dsa -N ""; ' - subprocess.call(('sh', '-c', "{ %s } </dev/null" % (genkeys))) - - try: - user = util.get_cfg_option_str(self.cfg,'user') - disable_root = util.get_cfg_option_bool(self.cfg, "disable_root", True) - keys = self.cloud.get_public_ssh_keys() - - if self.cfg.has_key("ssh_authorized_keys"): - cfgkeys = self.cfg["ssh_authorized_keys"] - keys.extend(cfgkeys) - - apply_credentials(keys,user,disable_root) - except: - warn("applying credentials failed!\n") - - send_ssh_keys_to_console() - - def h_ec2_ebs_mounts(self,name,args): - print "Warning, not doing anything for config %s" % name - - def h_config_setup_raid(self,name,args): - print "Warning, not doing anything for config %s" % name - - def h_config_runurl(self,name,args): - print "Warning, not doing anything for config %s" % name - - def h_config_mounts(self,name,args): - # handle 'mounts' - - # these are our default set of mounts - defmnts = [ [ "ephemeral0", "/mnt", "auto", "defaults", "0", "0" ], - [ "swap", "none", "swap", "sw", "0", "0" ] ] - - # fs_spec, fs_file, fs_vfstype, fs_mntops, fs-freq, fs_passno - defvals = [ None, None, "auto", "defaults", "0", "0" ] - - cfgmnt = [ ] - if self.cfg.has_key("mounts"): - cfgmnt = self.cfg["mounts"] - - for i in range(len(cfgmnt)): - # skip something that wasn't a list - if not isinstance(cfgmnt[i],list): continue - - # workaround, allow user to specify 'ephemeral' - # rather than more ec2 correct 'ephemeral0' - if cfgmnt[i] == "ephemeral": - cfgmnt[i] = "ephemeral0" - - newname = cfgmnt[i][0] - if not newname.startswith("/"): - newname = self.cloud.device_name_to_device(cfgmnt[i][0]) - if newname is not None: - cfgmnt[i][0] = newname - else: - # there is no good way of differenciating between - # a name that *couldn't* exist in the md service and - # one that merely didnt - # in order to allow user to specify 'sda3' rather - # than '/dev/sda3', go through some hoops - ok = False - for f in [ "/", "sd", "hd", "vd", "xvd" ]: - if cfgmnt[i][0].startswith(f): - ok = True - break - if not ok: - cfgmnt[i][1] = None - - for i in range(len(cfgmnt)): - # fill in values with - for j in range(len(defvals)): - if len(cfgmnt[i]) <= j: - cfgmnt[i].append(defvals[j]) - elif cfgmnt[i][j] is None: - cfgmnt[i][j] = defvals[j] - - if not cfgmnt[i][0].startswith("/"): - cfgmnt[i][0]="/dev/%s" % cfgmnt[i][0] - - # if the second entry in the list is 'None' this - # clears all previous entries of that same 'fs_spec' - # (fs_spec is the first field in /etc/fstab, ie, that device) - if cfgmnt[i][1] is None: - for j in range(i): - if cfgmnt[j][0] == cfgmnt[i][0]: - cfgmnt[j][1] = None - - - # for each of the "default" mounts, add them only if no other - # entry has the same device name - for defmnt in defmnts: - devname = self.cloud.device_name_to_device(defmnt[0]) - if devname is None: continue - if not devname.startswith("/"): - defmnt[0] = "/dev/%s" % devname - - cfgmnt_has = False - for cfgm in cfgmnt: - if cfgm[0] == defmnt[0]: - cfgmnt_has = True - break - - if cfgmnt_has: continue - cfgmnt.append(defmnt) - - - # now, each entry in the cfgmnt list has all fstab values - # if the second field is None (not the string, the value) we skip it - actlist = filter(lambda x: x[1] is not None, cfgmnt) - - if len(actlist) == 0: return - - needswap = False - dirs = [ ] - - fstab=file("/etc/fstab","ab") - fstab.write("# cloud-config mounts\n") - for line in actlist: - fstab.write('\t'.join(line) + "\n") - if line[2] == "swap": needswap = True - if line[1].startswith("/"): dirs.append(line[1]) - fstab.close() - - if needswap: - try: util.subp(("swapon", "-a")) - except: warn("Failed to enable swap") - - for d in dirs: - if os.path.exists(d): continue - try: os.makedirs(d) - except: warn("Failed to make '%s' config-mount\n",d) - - try: util.subp(("mount","-a")) - except: pass - - - - -def apply_credentials(keys, user, disable_root): - keys = set(keys) - if user: - setup_user_keys(keys, user, '') - - if disable_root: - key_prefix = 'command="echo \'Please login as the %s user rather than root user.\';echo;sleep 10" ' % user - else: - key_prefix = '' - - setup_user_keys(keys, 'root', key_prefix) - -def setup_user_keys(keys, user, key_prefix): - import pwd - saved_umask = os.umask(077) - - pwent = pwd.getpwnam(user) - - ssh_dir = '%s/.ssh' % pwent.pw_dir - if not os.path.exists(ssh_dir): - os.mkdir(ssh_dir) - os.chown(ssh_dir, pwent.pw_uid, pwent.pw_gid) - - authorized_keys = '%s/.ssh/authorized_keys' % pwent.pw_dir - fp = open(authorized_keys, 'a') - fp.write(''.join(['%s%s\n' % (key_prefix, key) for key in keys])) - fp.close() - - os.chown(authorized_keys, pwent.pw_uid, pwent.pw_gid) - - os.umask(saved_umask) - -def send_ssh_keys_to_console(): - send_keys_sh = """ - { - echo - echo "#############################################################" - echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" - ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub - ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub - echo "-----END SSH HOST KEY FINGERPRINTS-----" - echo "#############################################################" - } | logger -p user.info -s -t "ec2" - """ - subprocess.call(('sh', '-c', send_keys_sh)) - - -def warn(str): - sys.stderr.write("Warning:%s\n" % str) - -# srclist is a list of dictionaries, -# each entry must have: 'source' -# may have: key, ( keyid and keyserver) -def add_sources(srclist): - elst = [] - - for ent in srclist: - if not ent.has_key('source'): - elst.append([ "", "missing source" ]) - continue - - source=ent['source'] - if source.startswith("ppa:"): - try: util.subp(["add-apt-repository",source]) - except: - elst.append([source, "add-apt-repository failed"]) - continue - - if not ent.has_key('filename'): - ent['filename']='cloud_config_sources.list' - - if not ent['filename'].startswith("/"): - ent['filename'] = "%s/%s" % \ - ("/etc/apt/sources.list.d/", ent['filename']) - - if ( ent.has_key('keyid') and not ent.has_key('key') ): - ks = "keyserver.ubuntu.com" - if ent.has_key('keyserver'): ks = ent['keyserver'] - try: - ent['key'] = util.getkeybyid(ent['keyid'], ks) - except: - elst.append([source,"failed to get key from %s" % ks]) - continue - - if ent.has_key('key'): - try: util.subp(('apt-key', 'add', '-'), ent['key']) - except: - elst.append([source, "failed add key"]) - - try: util.write_file(ent['filename'], source + "\n") - except: - elst.append([source, "failed write to file %s" % ent['filename']]) - - return(elst) - - -def generate_sources_list(mirror): - stdout, stderr = subprocess.Popen(['lsb_release', '-cs'], stdout=subprocess.PIPE).communicate() - codename = stdout.strip() - - util.render_to_file('sources.list', '/etc/apt/sources.list', \ - { 'mirror' : mirror, 'codename' : codename }) |