chiark / gitweb /
auto-lookup vm provider based on available executables; more fault tolerant vagrant...
authorMichael Pöhn <michael.poehn@fsfe.org>
Sat, 1 Apr 2017 10:27:26 +0000 (12:27 +0200)
committerHans-Christoph Steiner <hans@eds.org>
Tue, 23 May 2017 18:06:06 +0000 (20:06 +0200)
fdroidserver/vmtools.py

index 1a72b49579353770e5c2e3114e762c02dccd4568..ba8008c4d768e3a880007ffec73aab798d658104 100644 (file)
@@ -31,6 +31,16 @@ from logging import getLogger
 logger = getLogger('fdroidserver-vmtools')
 
 
+def _check_call(cmd, shell=False):
+    logger.debug(' '.join(cmd))
+    return subprocess.check_call(cmd, shell=shell)
+
+
+def _check_output(cmd, shell=False):
+    logger.debug(' '.join(cmd))
+    return subprocess.check_output(cmd, shell=shell)
+
+
 def get_build_vm(srvdir, provider=None):
     """Factory function for getting FDroidBuildVm instances.
 
@@ -43,6 +53,8 @@ def get_build_vm(srvdir, provider=None):
     :returns: FDroidBuildVm instance.
     """
     abssrvdir = abspath(srvdir)
+
+    # use supplied provider
     if provider:
         if provider == 'libvirt':
             logger.debug('build vm provider \'libvirt\' selected')
@@ -52,6 +64,32 @@ def get_build_vm(srvdir, provider=None):
             return VirtualboxBuildVm(abssrvdir)
         else:
             logger.warn('build vm provider not supported: \'%s\'', provider)
+
+    # try guessing provider from installed software
+    try:
+        kvm_installed = 0 == _check_call(['which', 'kvm'])
+    except subprocess.CalledProcessError:
+        kvm_installed = False
+        try:
+            kvm_installed |= 0 == _check_call(['which', 'qemu'])
+        except subprocess.CalledProcessError:
+            pass
+    try:
+        vbox_installed = 0 == _check_call(['which', 'VBoxHeadless'], shell=True)
+    except subprocess.CalledProcessError:
+        vbox_installed = False
+    if kvm_installed and vbox_installed:
+        logger.debug('both kvm and vbox are installed.')
+    elif kvm_installed:
+        logger.debug('libvirt is the sole installed and supported vagrant provider, selecting \'libvirt\'')
+        return LibvirtBuildVm(abssrvdir)
+    elif vbox_installed:
+        logger.debug('virtualbox is the sole installed and supported vagrant provider, selecting \'virtualbox\'')
+        return VirtualboxBuildVm(abssrvdir)
+    else:
+        logger.debug('could not confirm that either virtualbox or kvm/libvirt are installed')
+
+    # try guessing provider from .../srvdir/.vagrant internals
     has_libvirt_machine = isdir(joinpath(abssrvdir, '.vagrant',
                                          'machines', 'default', 'libvirt'))
     has_vbox_machine = isdir(joinpath(abssrvdir, '.vagrant',
@@ -128,12 +166,16 @@ class FDroidBuildVm():
         except Exception as e:
             logger.debug("could not delete vagrant dir: %s, %s", vgrntdir, e)
         try:
-            self._check_call(['vagrant', 'global-status', '--prune'])
+            _check_call(['vagrant', 'global-status', '--prune'])
         except subprocess.CalledProcessError as e:
             logger.debug('pruning global vagrant status failed: %s', e)
 
-    def package(self, output=None, keep_box_file=None):
-        self.vgrnt.package(output=output)
+    def package(self, output=None, vagrantfile=None, keep_box_file=None):
+        previous_tmp_dir = joinpath(self.srvdir, '_tmp_package')
+        if isdir(previous_tmp_dir):
+            logger.info('found previous vagrant package temp dir \'%s\', deleting it', previous_tmp_dir)
+            shutil.rmtree(previous_tmp_dir)
+        self.vgrnt.package(output=output, vagrantfile=vagrantfile)
 
     def box_add(self, boxname, boxfile, force=True):
         """Add vagrant box to vagrant.
@@ -149,20 +191,12 @@ class FDroidBuildVm():
 
     def box_remove(self, boxname):
         try:
-            self._check_call(['vagrant', 'box', 'remove', '--all', '--force', boxname])
+            _check_call(['vagrant', 'box', 'remove', '--all', '--force', boxname])
         except subprocess.CalledProcessError as e:
             logger.debug('tried removing box %s, but is did not exist: %s', boxname, e)
             # TODO: remove box files manually
             # nesessary when Vagrantfile in ~/.vagrant.d/... is broken.
 
-    def _check_call(self, cmd, shell=False):
-        logger.debug(' '.join(cmd))
-        return subprocess.check_call(cmd, shell=shell)
-
-    def _check_output(self, cmd, shell=False):
-        logger.debug(' '.join(cmd))
-        return subprocess.check_output(cmd, shell=shell)
-
 
 class LibvirtBuildVm(FDroidBuildVm):
     def __init__(self, srvdir):
@@ -182,7 +216,7 @@ class LibvirtBuildVm(FDroidBuildVm):
         # this is way more easy and therefore fault tolerant.
         # (eg. lookupByName only works on running VMs)
         try:
-            self._check_call(('virsh', '-c', 'qemu:///system', 'destroy', self.srvname))
+            _check_call(('virsh', '-c', 'qemu:///system', 'destroy', self.srvname))
             logger.info("...waiting a sec...")
             time.sleep(10)
         except subprocess.CalledProcessError as e:
@@ -190,7 +224,7 @@ class LibvirtBuildVm(FDroidBuildVm):
         try:
             # libvirt python bindings do not support all flags required
             # for undefining domains correctly.
-            self._check_call(('virsh', '-c', 'qemu:///system', 'undefine', self.srvname, '--nvram', '--managed-save', '--remove-all-storage', '--snapshots-metadata'))
+            _check_call(('virsh', '-c', 'qemu:///system', 'undefine', self.srvname, '--nvram', '--managed-save', '--remove-all-storage', '--snapshots-metadata'))
             logger.info("...waiting a sec...")
             time.sleep(10)
         except subprocess.CalledProcessError as e:
@@ -217,10 +251,10 @@ class LibvirtBuildVm(FDroidBuildVm):
             vol = storagePool.storageVolLookupByName(self.srvname + '.img')
             imagepath = vol.path()
             # TODO use a libvirt storage pool to ensure the img file is readable
-            self._check_call(['sudo', '/bin/chmod', '-R', 'a+rX', '/var/lib/libvirt/images'])
+            _check_call(['sudo', '/bin/chmod', '-R', 'a+rX', '/var/lib/libvirt/images'])
             shutil.copy2(imagepath, 'box.img')
-            self._check_call(['qemu-img', 'rebase', '-p', '-b', '', 'box.img'])
-            img_info_raw = self._check_output(['qemu-img', 'info', '--output=json', 'box.img'])
+            _check_call(['qemu-img', 'rebase', '-p', '-b', '', 'box.img'])
+            img_info_raw = _check_output(['qemu-img', 'info', '--output=json', 'box.img'])
             img_info = json.loads(img_info_raw.decode('utf-8'))
             metadata = {"provider": "libvirt",
                         "format": img_info['format'],
@@ -258,7 +292,7 @@ class LibvirtBuildVm(FDroidBuildVm):
     def box_remove(self, boxname):
         super().box_remove(boxname)
         try:
-            self._check_call(['virsh', '-c', 'qemu:///system', 'vol-delete', '--pool', 'default', '%s_vagrant_box_image_0.img' % (boxname)])
+            _check_call(['virsh', '-c', 'qemu:///system', 'vol-delete', '--pool', 'default', '%s_vagrant_box_image_0.img' % (boxname)])
         except subprocess.CalledProcessError as e:
             logger.info('tired removing \'%s\', file was not present in first place: %s', boxname, e)