# # 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 cloudinit import cloudinit.util as util import pwd import socket import subprocess import os import glob import sys import time per_instance="once-per-instance" cronpre = "/etc/cron.d/cloudinit" class CloudConfig(): cfgfile = None handlers = { } cfg = None def __init__(self,cfgfile): self.cloud = cloudinit.CloudInit() 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') self.add_handler('config-puppet') self.add_handler('config-misc') 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 }