Package VMBuilder :: Module util
[frames] | no frames]

Source Code for Module VMBuilder.util

  1  # 
  2  #    Uncomplicated VM Builder 
  3  #    Copyright (C) 2007-2009 Canonical Ltd. 
  4  # 
  5  #    See AUTHORS for list of contributors 
  6  # 
  7  #    This program is free software: you can redistribute it and/or modify 
  8  #    it under the terms of the GNU General Public License version 3, as 
  9  #    published by the Free Software Foundation. 
 10  # 
 11  #    This program is distributed in the hope that it will be useful, 
 12  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  #    GNU General Public License for more details. 
 15  # 
 16  #    You should have received a copy of the GNU General Public License 
 17  #    along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 18  # 
 19  #    Various utility functions 
 20  import ConfigParser 
 21  import errno 
 22  import fcntl 
 23  import logging 
 24  import os.path 
 25  import select 
 26  import subprocess 
 27  import tempfile 
 28  from   exception        import VMBuilderException, VMBuilderUserError 
 29   
30 -class NonBlockingFile(object):
31 - def __init__(self, fp, logfunc):
32 self.file = fp 33 self.set_non_blocking() 34 self.buf = '' 35 self.logbuf = '' 36 self.logfunc = logfunc
37
38 - def set_non_blocking(self):
39 flags = fcntl.fcntl(self.file, fcntl.F_GETFL) 40 flags = flags | os.O_NONBLOCK 41 fcntl.fcntl(self.file, fcntl.F_SETFL, flags)
42
43 - def __getattr__(self, attr):
44 if attr == 'closed': 45 return self.file.closed 46 else: 47 raise AttributeError()
48
49 - def process_input(self):
50 data = self.file.read() 51 if data == '': 52 self.file.close() 53 if self.logbuf: 54 self.logfunc(self.logbuf) 55 else: 56 self.buf += data 57 self.logbuf += data 58 while '\n' in self.logbuf: 59 line, self.logbuf = self.logbuf.split('\n', 1) 60 self.logfunc(line)
61
62 -def run_cmd(*argv, **kwargs):
63 """ 64 Runs a command. 65 66 Locale is reset to C to make parsing error messages possible. 67 68 @type stdin: string 69 @param stdin: input to provide to the process on stdin. If None, process' 70 stdin will be attached to /dev/null 71 @type ignore_fail: boolean 72 @param ignore_fail: If True, a non-zero exit code from the command will not 73 cause an exception to be raised. 74 @type env: dict 75 @param env: Dictionary of extra environment variables to set in the new process 76 77 @rtype: string 78 @return: string containing the stdout of the process 79 """ 80 81 env = kwargs.get('env', {}) 82 stdin = kwargs.get('stdin', None) 83 ignore_fail = kwargs.get('ignore_fail', False) 84 args = [str(arg) for arg in argv] 85 logging.debug(args.__repr__()) 86 if stdin: 87 logging.debug('stdin was set and it was a string: %s' % (stdin,)) 88 stdin_arg = subprocess.PIPE 89 else: 90 stdin_arg = file('/dev/null', 'r') 91 proc_env = dict(os.environ) 92 proc_env['LANG'] = 'C' 93 proc_env['LC_ALL'] = 'C' 94 proc_env.update(env) 95 96 try: 97 proc = subprocess.Popen(args, stdin=stdin_arg, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=proc_env) 98 except OSError, error: 99 if error.errno == errno.ENOENT: 100 raise VMBuilderUserError, "Couldn't find the program '%s' on your system" % (argv[0]) 101 else: 102 raise VMBuilderUserError, "Couldn't launch the program '%s': %s" % (argv[0], error) 103 104 if stdin: 105 proc.stdin.write(stdin) 106 proc.stdin.close() 107 108 mystdout = NonBlockingFile(proc.stdout, logfunc=logging.debug) 109 mystderr = NonBlockingFile(proc.stderr, logfunc=(ignore_fail and logging.debug or logging.info)) 110 111 while not (mystdout.closed and mystderr.closed): 112 # Block until either of them has something to offer 113 fds = select.select([x.file for x in [mystdout, mystderr] if not x.closed], [], [])[0] 114 for fp in [mystderr, mystdout]: 115 if fp.file in fds: 116 fp.process_input() 117 118 status = proc.wait() 119 if not ignore_fail and status != 0: 120 raise VMBuilderException, "Process (%s) returned %d. stdout: %s, stderr: %s" % (args.__repr__(), status, mystdout.buf, mystderr.buf) 121 return mystdout.buf
122
123 -def checkroot():
124 """ 125 Check if we're running as root, and bail out if we're not. 126 """ 127 128 if os.geteuid() != 0: 129 raise VMBuilderUserError("This script must be run as root (e.g. via sudo)")
130
131 -def render_template(plugin, context, tmplname, extra_context=None):
132 # Import here to avoid having to build-dep on python-cheetah 133 from Cheetah.Template import Template 134 searchList = [] 135 if context: 136 searchList.append(extra_context) 137 searchList.append(context) 138 139 # tmpldirs.insert(0,'%s/%%s' % vm.templates) 140 141 tmpldirs = [dir % plugin for dir in context.template_dirs] 142 143 for dir in tmpldirs: 144 tmplfile = '%s/%s.tmpl' % (dir, tmplname) 145 if os.path.exists(tmplfile): 146 t = Template(file=tmplfile, searchList=searchList) 147 output = t.respond() 148 logging.debug('Output from template \'%s\': %s' % (tmplfile, output)) 149 return output 150 151 raise VMBuilderException('Template %s.tmpl not found in any of %s' % (tmplname, ', '.join(tmpldirs)))
152
153 -def call_hooks(context, func, *args, **kwargs):
154 logging.info('Calling hook: %s' % func) 155 logging.debug('(args=%r, kwargs=%r)' % (args, kwargs)) 156 for plugin in context.plugins: 157 logging.debug('Calling %s method in %s plugin.' % (func, plugin.__module__)) 158 getattr(plugin, func, log_no_such_method)(*args, **kwargs) 159 160 for f in context.hooks.get(func, []): 161 logging.debug('Calling %r.' % (f,)) 162 f(*args, **kwargs) 163 164 logging.debug('Calling %s method in context plugin %s.' % (func, context.__module__)) 165 getattr(context, func, log_no_such_method)(*args, **kwargs)
166
167 -def log_no_such_method(*args, **kwargs):
168 logging.debug('No such method') 169 return
170
171 -def tmp_filename(suffix='', tmp_root=None):
172 # There is a risk in using tempfile.mktemp(): it's not recommended 173 # to run vmbuilder on machines with untrusted users. 174 return tempfile.mktemp(suffix=suffix, dir=tmp_root)
175
176 -def tmpdir(suffix='', tmp_root=None):
177 return tempfile.mkdtemp(suffix=suffix, dir=tmp_root)
178
179 -def set_up_tmpfs(tmp_root=None, size=1024):
180 """Sets up a tmpfs storage under `tmp_root` with the size of `size` MB. 181 182 `tmp_root` defaults to tempfile.gettempdir(). 183 """ 184 mount_point = tmpdir('tmpfs', tmp_root) 185 mount_cmd = ["mount", "-t", "tmpfs", 186 "-o", "size=%dM,mode=0770" % int(size), 187 "tmpfs", mount_point ] 188 logging.info('Mounting tmpfs under %s' % mount_point) 189 logging.debug('Executing: %s' % mount_cmd) 190 run_cmd(*mount_cmd) 191 192 return mount_point
193
194 -def clean_up_tmpfs(mount_point):
195 """Unmounts a tmpfs storage under `mount_point`.""" 196 umount_cmd = ["umount", "-t", "tmpfs", mount_point ] 197 logging.info('Unmounting tmpfs from %s' % mount_point) 198 logging.debug('Executing: %s' % umount_cmd) 199 run_cmd(*umount_cmd)
200 201
202 -def get_conf_value(context, confparser, key):
203 confvalue = None 204 try: 205 confvalue = confparser.get('DEFAULT', key) 206 except ConfigParser.NoSectionError: 207 pass 208 except ConfigParser.NoOptionError: 209 pass 210 211 if confparser.has_option(context.arg, key): 212 confvalue = confparser.get(context.arg, key) 213 214 logging.debug('Returning value %s for configuration key %s' % (repr(confvalue), key)) 215 return confvalue
216
217 -def apply_config_files_to_context(config_files, context):
218 confparser = ConfigParser.SafeConfigParser() 219 confparser.read(config_files) 220 221 for (key, setting) in context._config.iteritems(): 222 confvalue = get_conf_value(context, confparser, key) 223 if confvalue: 224 setting.set_value_fuzzy(confvalue)
225