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

Source Code for Module VMBuilder.vm

  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  #    The VM class 
 20  import ConfigParser 
 21  from   gettext             import gettext 
 22  import logging 
 23  import os 
 24  import optparse 
 25  import textwrap 
 26  import urllib 
 27  import VMBuilder 
 28  import VMBuilder.util      as util 
 29  import VMBuilder.log       as log 
 30  import VMBuilder.disk      as disk 
 31  from   VMBuilder.disk      import Disk, Filesystem 
 32  from   VMBuilder.exception import VMBuilderException, VMBuilderUserError 
 33  _ = gettext 
 34   
35 -class VM(object):
36 """The VM object has the following attributes of relevance to plugins: 37 38 distro: A distro object, representing the distro running in the vm 39 40 disks: The disk images for the vm. 41 filesystems: The filesystem images for the vm. 42 43 result_files: A list of the files that make up the entire vm. 44 The ownership of these files will be fixed up. 45 46 optparser: Will be of interest mostly to frontends. Any sort of option 47 a plugin accepts will be represented in the optparser. 48 49 50 """
51 - def __init__(self, conf=None):
52 self.hypervisor = None #: hypervisor object, representing the hypervisor the vm is destined for 53 self.distro = None 54 55 self.disks = [] 56 self.filesystems = [] 57 58 self.result_files = [] 59 self.plugins = [] 60 self._cleanup_cbs = [] 61 62 #: final destination for the disk images 63 self.destdir = None 64 #: tempdir where we do all the work 65 self.workdir = None 66 #: mount point where the disk images will be mounted 67 self.rootmnt = None 68 #: directory where we build up the guest filesystem 69 self.tmproot = None 70 71 self.fsmounted = False 72 73 self.optparser = _MyOptParser(epilog="ubuntu-vm-builder is Copyright (C) 2007-2009 Canonical Ltd. and written by Soren Hansen <soren@linux2go.dk>.", usage='%prog hypervisor distro [options]') 74 self.optparser.arg_help = (('hypervisor', self.hypervisor_help), ('distro', self.distro_help)) 75 76 self.confparser = ConfigParser.SafeConfigParser() 77 78 if conf: 79 if not(os.path.isfile(conf)): 80 raise VMBuilderUserError('The path to the configuration file is not valid: %s.' % conf) 81 else: 82 conf = '' 83 84 self.confparser.read(['/etc/vmbuilder.cfg', os.path.expanduser('~/.vmbuilder.cfg'), conf]) 85 86 self._register_base_settings() 87 88 self.add_clean_cmd('rm', log.logfile)
89
90 - def distro_help(self):
91 return 'Distro. Valid options: %s' % " ".join(VMBuilder.distros.keys())
92
93 - def hypervisor_help(self):
94 return 'Hypervisor. Valid options: %s' % " ".join(VMBuilder.hypervisors.keys())
95
96 - def register_setting(self, *args, **kwargs):
97 return self.optparser.add_option(*args, **kwargs)
98
99 - def register_setting_group(self, group):
100 return self.optparser.add_option_group(group)
101
102 - def setting_group(self, *args, **kwargs):
103 return optparse.OptionGroup(self.optparser, *args, **kwargs)
104
105 - def _register_base_settings(self):
106 self.register_setting('-d', '--dest', dest='destdir', help='Specify the destination directory. [default: <hypervisor>-<distro>].') 107 self.register_setting('-c', '--config', type='string', help='Specify a additional configuration file') 108 self.register_setting('--debug', action='callback', callback=log.set_verbosity, help='Show debug information') 109 self.register_setting('-v', '--verbose', action='callback', callback=log.set_verbosity, help='Show progress information') 110 self.register_setting('-q', '--quiet', action='callback', callback=log.set_verbosity, help='Silent operation') 111 self.register_setting('-t', '--tmp', default=os.environ.get('TMPDIR', '/tmp'), help='Use TMP as temporary working space for image generation. Defaults to $TMPDIR if it is defined or /tmp otherwise. [default: %default]') 112 self.register_setting('--templates', metavar='DIR', help='Prepend DIR to template search path.') 113 self.register_setting('-o', '--overwrite', action='store_true', default=False, help='Force overwrite of destination directory if it already exist. [default: %default]') 114 self.register_setting('--in-place', action='store_true', default=False, help='Install directly into the filesystem images. This is needed if your $TMPDIR is nodev and/or nosuid, but will result in slightly larger file system images.') 115 self.register_setting('--tmpfs', metavar="OPTS", help='Use a tmpfs as the working directory, specifying its size or "-" to use tmpfs default (suid,dev,size=1G).') 116 self.register_setting('-m', '--mem', type='int', default=128, help='Assign MEM megabytes of memory to the guest vm. [default: %default]')
117
118 - def add_disk(self, *args, **kwargs):
119 """Adds a disk image to the virtual machine""" 120 disk = Disk(self, *args, **kwargs) 121 self.disks.append(disk) 122 return disk
123
124 - def add_filesystem(self, *args, **kwargs):
125 """Adds a filesystem to the virtual machine""" 126 fs = Filesystem(self, *args, **kwargs) 127 self.filesystems.append(fs) 128 return fs
129
130 - def call_hooks(self, func):
131 for plugin in self.plugins: 132 getattr(plugin, func)() 133 getattr(self.hypervisor, func)() 134 getattr(self.distro, func)()
135
136 - def preflight_check(self):
137 for opt in sum([self.confparser.options(section) for section in self.confparser.sections()], []) + [k for (k,v) in self.confparser.defaults().iteritems()]: 138 if '-' in opt: 139 raise VMBuilderUserError('You specified a "%s" config option in a config file, but that is not valid. Perhaps you meant "%s"?' % (opt, opt.replace('-', '_'))) 140 141 self.call_hooks('preflight_check') 142 143 # Check repository availability 144 if self.mirror: 145 testurl = self.mirror 146 else: 147 testurl = 'http://archive.ubuntu.com/' 148 149 try: 150 logging.debug('Testing access to %s' % testurl) 151 testnet = urllib.urlopen(testurl) 152 except IOError: 153 raise VMBuilderUserError('Could not connect to %s. Please check your connectivity and try again.' % testurl) 154 155 testnet.close()
156
157 - def create(self):
158 """ 159 The core vm creation method 160 161 The VM creation happens in the following steps: 162 163 A series of preliminary checks are performed: 164 - We check if we're being run as root, since 165 the filesystem handling requires root priv's 166 - Each plugin's preflight_check method is called. 167 See L{VMBuilder.plugins.Plugin} documentation for details 168 - L{create_directory_structure} is called 169 - VMBuilder.disk.create_partitions is called 170 - VMBuilder.disk.create_filesystems is called 171 - .mount_partitions is called 172 - .install is called 173 174 """ 175 util.checkroot() 176 177 finished = False 178 try: 179 self.preflight_check() 180 self.create_directory_structure() 181 182 disk.create_partitions(self) 183 disk.create_filesystems(self) 184 self.mount_partitions() 185 186 self.install() 187 188 self.umount_partitions() 189 190 self.hypervisor.finalize() 191 192 self.deploy() 193 194 util.fix_ownership(self.result_files) 195 196 finished = True 197 except VMBuilderException: 198 raise 199 finally: 200 if not finished: 201 logging.debug("Oh, dear, an exception occurred") 202 self.cleanup() 203 204 if not finished: 205 return(1) 206 return(0)
207
208 -class _MyOptParser(optparse.OptionParser):
209 - def format_arg_help(self, formatter):
210 result = [] 211 for arg in self.arg_help: 212 result.append(self.format_arg(formatter, arg)) 213 return "".join(result)
214
215 - def format_arg(self, formatter, arg):
216 result = [] 217 arghelp = arg[1]() 218 arg = arg[0] 219 width = formatter.help_position - formatter.current_indent - 2 220 if len(arg) > width: 221 arg = "%*s%s\n" % (self.current_indent, "", arg) 222 indent_first = formatter.help_position 223 else: # start help on same line as opts 224 arg = "%*s%-*s " % (formatter.current_indent, "", width, arg) 225 indent_first = 0 226 result.append(arg) 227 help_lines = textwrap.wrap(arghelp, formatter.help_width) 228 result.append("%*s%s\n" % (indent_first, "", help_lines[0])) 229 result.extend(["%*s%s\n" % (formatter.help_position, "", line) 230 for line in help_lines[1:]]) 231 return "".join(result)
232
233 - def format_option_help(self, formatter=None):
234 if formatter is None: 235 formatter = self.formatter 236 formatter.store_option_strings(self) 237 result = [] 238 if self.arg_help: 239 result.append(formatter.format_heading(_("Arguments"))) 240 formatter.indent() 241 result.append(self.format_arg_help(formatter)) 242 result.append("\n") 243 result.append("*** Use vmbuilder <hypervisor> <distro> --help to get more options. Hypervisor, distro, and plugins specific help is only available when the first two arguments are supplied.\n") 244 result.append("\n") 245 formatter.dedent() 246 result.append(formatter.format_heading(_("Options"))) 247 formatter.indent() 248 if self.option_list: 249 result.append(optparse.OptionContainer.format_option_help(self, formatter)) 250 result.append("\n") 251 for group in self.option_groups: 252 result.append(group.format_help(formatter)) 253 result.append("\n") 254 formatter.dedent() 255 # Drop the last "\n", or the header if no options or option groups: 256 return "".join(result[:-1])
257