Browse Source

qubes/vm/dispvm: Add methods for creating and destroying

fixes QubesOS/qubes-issues#866
Wojtek Porczyk 8 years ago
parent
commit
5a76d0b03b
4 changed files with 63 additions and 0 deletions
  1. 12 0
      qubes/app.py
  2. 1 0
      qubes/config.py
  3. 1 0
      qubes/vm/appvm.py
  4. 49 0
      qubes/vm/dispvm.py

+ 12 - 0
qubes/app.py

@@ -30,9 +30,11 @@ import functools
 import grp
 import logging
 import os
+import random
 import sys
 import tempfile
 import time
+import uuid
 
 import jinja2
 import libvirt
@@ -485,6 +487,16 @@ class VMCollection(object):
         raise LookupError("Cannot find unused netid!")
 
 
+    def get_new_unused_dispid(self):
+        for i in range(qubes.config.max_dispid ** 0.5):
+            dispid = random.SystemRandom().randrange(qubes.config.max_dispid)
+            if not any(getattr(vm, 'dispid', None) == dispid for vm in self):
+                return dispid
+        raise LookupError((
+            'https://xkcd.com/221/',
+            'http://dilbert.com/strip/2001-10-25')[random.randint(0, 1)])
+
+
 class Qubes(qubes.PropertyHolder):
     '''Main Qubes application
 

+ 1 - 0
qubes/config.py

@@ -111,3 +111,4 @@ defaults = {
 
 max_qid = 254
 max_netid = 254
+max_dispid = 10000

+ 1 - 0
qubes/vm/appvm.py

@@ -3,6 +3,7 @@
 
 import qubes.events
 import qubes.vm.qubesvm
+
 from qubes.config import defaults
 
 

+ 49 - 0
qubes/vm/dispvm.py

@@ -1,6 +1,8 @@
 #!/usr/bin/python2 -O
 # vim: fileencoding=utf-8
 
+import random
+
 import qubes.vm.qubesvm
 import qubes.vm.appvm
 import qubes.config
@@ -44,6 +46,7 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
                 'volume_type': 'read-only',
             }
         }
+
         super(DispVM, self).__init__(*args, **kwargs)
 
     @qubes.events.handler('domain-load')
@@ -52,3 +55,49 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
         # Some additional checks for template based VM
         assert self.template
         # self.template.appvms.add(self) # XXX
+
+
+    @classmethod
+    def from_appvm(cls, appvm, **kwargs):
+        '''Create a new instance from given AppVM
+
+        :param qubes.vm.appvm.AppVM appvm: template from which the VM should \
+            be created (could also be name or qid)
+        :returns: new disposable vm
+
+        *kwargs* are passed to the newly created VM
+
+        >>> import qubes.vm.dispvm.DispVM
+        >>> dispvm = qubes.vm.dispvm.DispVM.from_appvm(appvm).start()
+        >>> dispvm.run_service('qubes.VMShell', input='firefox')
+        >>> dispvm.cleanup()
+
+        This method modifies :file:`qubes.xml` file. In fact, the newly created
+        vm belongs to other :py:class:`qubes.Qubes` instance than the *app*.
+        The qube returned is not started.
+        '''
+        store = appvm.app.store if isinstance(appvm, qubes.vm.BaseVM) else None
+        app = qubes.Qubes(store)
+        dispvm = app.add_new_vm(
+            cls,
+            dispid=app.domains.get_new_unused_dispid(),
+            template=app.domains[appvm],
+            **kwargs)
+        dispvm.create_on_disk()
+        app.save()
+        return dispvm
+
+
+    def cleanup(self):
+        '''Clean up after the DispVM
+
+        This stops the disposable qube and removes it from the store.
+
+        This method modifies :file:`qubes.xml` file.
+        '''
+        app = qubes.Qubes(self.app.store)
+        self = app.domains[self.uuid]
+        self.force_shutdown()
+        self.remove_from_disk()
+        del app.domains[self]
+        app.save()