~vila/+junk/test-smart-scopes

« back to all changes in this revision

Viewing changes to bin/setup_vm.py

  • Committer: Vincent Ladeuil
  • Date: 2013-02-06 14:06:38 UTC
  • Revision ID: vila+qa@canonical.com-20130206140638-v41gl4d8nyn5r5wg
Add basic commands to install vms.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
to get them.
7
7
 
8
8
"""
 
9
import argparse
9
10
import os
10
11
import subprocess
11
12
import sys
153
154
ssh_authorized_keys:
154
155
 - {vm.ssh_pub_key}
155
156
 
156
 
#apt_proxy: http://10.189.74.2:8000
157
157
apt_sources: 
158
158
 - source: deb http://archive.ubuntu.com/ubuntu {vm.release} restricted multiverse
159
159
 - source: deb-src http://archive.ubuntu.com/ubuntu {vm.release} restricted multiverse
176
176
        ))
177
177
 
178
178
 
179
 
class CommandError(StandardError):
180
 
 
181
 
    def __init__(self, cmd, retcode, out, err):
182
 
        joined_cmd = ' '.join(cmd)
183
 
        msg = '''
 
179
class SetupVmError(Exception):
 
180
 
 
181
    msg = 'setup_vm Generic Error: %r'
 
182
 
 
183
    def __init__(self, msg=None, **kwds):
 
184
        if msg is not None:
 
185
            self.msg = msg
 
186
        for key, value in kwds.items():
 
187
            setattr(self, key, value)
 
188
 
 
189
    def __str__(self):
 
190
        return self.msg.format((), **self.__dict__)
 
191
 
 
192
    __repr__ = __str__
 
193
 
 
194
 
 
195
class CommandError(SetupVmError):
 
196
 
 
197
    msg = '''
184
198
  command: {joined_cmd}
185
199
  retcode: {retcode}
186
200
  output: {out}
187
201
  error: {err}
188
 
'''.format((), **locals())
189
 
        super(CommandError, self).__init__(msg)
 
202
'''
 
203
 
 
204
    def __init__(self, cmd, retcode, out, err):
 
205
        super(CommandError, self).__init__(joined_cmd=' '.join(cmd),
 
206
                                           retcode=retcode, err=err, out=out)
 
207
 
190
208
 
191
209
def run_subprocess(args):
192
210
    proc = subprocess.Popen(args,
231
249
        self._seed_path = None
232
250
        self._disk_image_path = None
233
251
 
234
 
    def _download_in_cache(self, source_url, name):
 
252
    def _download_in_cache(self, source_url, name, force=False):
235
253
        """Download ``name`` from ``source_url`` in ``vm.download_cache``.
236
254
 
 
255
        :param source_url: The url where the file to download is located
 
256
 
 
257
        :param name: The name of the file to download (also used as the name
 
258
            for the downloaded file).
 
259
 
 
260
        :param force: Remove the file from the cache if present.
 
261
 
237
262
        :return: False if the file is in the download cache, True if a download
238
263
            occurred.
239
264
        """
240
265
        source = urlutils.join(source_url, name)
241
266
        download_dir = self.conf.get('vm.download_cache')
242
267
        target = os.path.join(download_dir, name)
 
268
        # FIXME: By default the download dir may be under root control, but if
 
269
        # a user chose to use a different one under his own control, it would
 
270
        # be nice to not require sudo usage. -- vila 2013-02-06
 
271
        if force:
 
272
            run_subprocess(['sudo', 'rm', '-f', target])
243
273
        if not os.path.exists(target):
244
 
            run_subprocess(['wget', '--progress=dot:mega','-O', target, source])
 
274
            run_subprocess(['sudo', 'wget', '--progress=dot:mega','-O',
 
275
                            target, source])
245
276
            return True
246
277
        else:
247
278
            return False
248
279
 
249
 
    def download_iso(self):
 
280
    def download_iso(self, force=False):
250
281
        """Download the iso to install the vm.
251
282
 
252
283
        :return: False if the iso is in the download cache, True if a download
253
284
            occurred.
254
285
        """
255
286
        return self._download_in_cache(self.conf.get('vm.iso_url'),
256
 
                                       self.conf.get('vm.iso_name'))
 
287
                                       self.conf.get('vm.iso_name'),
 
288
                                       force=force)
257
289
 
258
 
    def download_cloud_image(self):
 
290
    def download_cloud_image(self, force=False):
259
291
        """Download the cloud image to install the vm.
260
292
 
261
293
        :return: False if the image is in the download cache, True if a
262
294
            download occurred.
263
295
        """
264
296
        return self._download_in_cache(self.conf.get('vm.cloud_image_url'),
265
 
                                       self.conf.get('vm.cloud_image_name'))
 
297
                                       self.conf.get('vm.cloud_image_name'),
 
298
                                       force=force)
266
299
 
267
300
    def _create_tempfile_with_content(self, content):
268
301
        temp = tempfile.NamedTemporaryFile()
338
371
             '--boot', 'hd', '--hvm',
339
372
             ])
340
373
        # Urgh, had to wait for virt-install to create the vm definition.
 
374
        # FIXME: using python-libvirt directly may help ? -- vila 2013-02-05
341
375
        time.sleep(2.0)
342
376
        # While `install` is running, let's connect to the console
343
377
        console_cmd = ['virsh', 'console', self.conf.get('vm.name')]
372
406
            ['sudo', 'virsh', 'undefine', self.conf.get('vm.name'),
373
407
             '--remove-all-storage'])
374
408
 
 
409
 
 
410
class ArgParser(argparse.ArgumentParser):
 
411
    """A parser for the setup_vm script."""
 
412
 
 
413
    def __init__(self):
 
414
        description = 'Set up virtual machines from a configuration file.'
 
415
        super(ArgParser, self).__init__(
 
416
            prog='setup_vm.py', description=description)
 
417
        self.add_argument(
 
418
            'name', help='Virtual machine section in the configuration file.')
 
419
        self.add_argument('--download', '-d', action="store_true",
 
420
                          help='Force download of the required image.')
 
421
        self.add_argument('--install', '-i', action="store_true",
 
422
                          help='Install the virtual machine.')
 
423
 
 
424
    def parse_args(self, args=None, out=None, err=None):
 
425
        """Parse arguments, overridding stdout/stderr if provided.
 
426
 
 
427
        Overridding stdout/stderr is provided for tests.
 
428
 
 
429
        :params args: Defaults to sys.argv[1:].
 
430
 
 
431
        :param out: Defaults to sys.stdout.
 
432
 
 
433
        :param err: Defaults to sys.stderr.
 
434
        """
 
435
        out_orig = sys.stdout
 
436
        err_orig = sys.stderr
 
437
        try:
 
438
            if out is not None:
 
439
                sys.stdout = out
 
440
            if err is not None:
 
441
                sys.stderr = err
 
442
            return super(ArgParser, self).parse_args(args)
 
443
        finally:
 
444
            sys.stdout = out_orig
 
445
            sys.stderr = err_orig
 
446
 
 
447
 
 
448
 
 
449
arg_parser = ArgParser()
 
450
 
 
451
class Command(object):
 
452
 
 
453
    def __init__(self, vm):
 
454
        self.vm = vm
 
455
 
 
456
 
 
457
class Download(Command):
 
458
 
 
459
    def run(self):
 
460
        # FIXME: what needs to be downloaded should depend on the type of the
 
461
        # vm (possibly errors if there is nothing to download). -- vila
 
462
        # 2013-02-06
 
463
        self.vm.download_cloud_image(force=True)
 
464
 
 
465
 
 
466
class Install(Command):
 
467
 
 
468
    def run(self):
 
469
        # FIXME: Should we check whether the vm already exist and delete it or
 
470
        # should we delete it unconditionally ? -- vila 2013-02-06
 
471
        self.vm.undefine()
 
472
        # FIXME: The installation method may vary depending on the vm type.
 
473
        # -- vila 2013-02-06
 
474
        self.vm.install_with_seed()
 
475
        self.vm.poweroff()
 
476
 
 
477
 
 
478
 
 
479
def build_commands(args=None, out=None, err=None):
 
480
    cmds = []
 
481
    if args is None:
 
482
        args = sys.argv[1:]
 
483
 
 
484
    ns = arg_parser.parse_args(args, out=out, err=err)
 
485
 
 
486
    conf = VmStack(ns.name)
 
487
    vm = Kvm(conf)
 
488
    if ns.download:
 
489
        cmds.append(Download(vm))
 
490
    if ns.install:
 
491
        cmds.append(Install(vm))
 
492
    return cmds
 
493
 
 
494
 
 
495
def run(args=None):
 
496
    cmds = build_commands(args)
 
497
    for cmd in cmds:
 
498
        try:
 
499
            cmd.run()
 
500
        except SetupVmError, e:
 
501
            # Stop on first error
 
502
            print 'ERROR: %s' % e
 
503
            exit(-1)
 
504
 
 
505
 
 
506
if __name__ == "__main__":
 
507
    run()