# # Common code for the EC2 configuration files in Ubuntu # Copyright (C) 2008-2010 Canonical Ltd. # # Author: Chuck Short # # 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 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') 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 add_ppa(self): #value = self.cfg['apt_sources'] for ent in self.cfg['apt_sources']: ppa = ent['source'] where = ppa.find('ppa:') if where != -1: return ppa def add_custom_repo(self): sources = [] value = self.cfg['apt_sources'] for ent in self.cfg['apt_sources']: if ent.has_key('keyserver'): keyserver = ent['keyserver'] if ent.has_key('keyid'): keyid = ent['keyid'] if ent.has_key('filename'): filename = ent['filename'] source = ent['source'] if source.startswith("deb"): sources.append(source) return (keyserver,sources,keyid,filename) 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) 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="iptables -A OUTPUT -p tcp --dport 80 --destination 169.254.169.254 -j REJECT" 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 }