Browse Source

core3: fixes from Marek

This is adapted from commit 90a50dca406e3d40c88ea338566e0460589df7a3.
Wojtek Porczyk 8 years ago
parent
commit
80d664441d
8 changed files with 134 additions and 49 deletions
  1. 18 9
      qubes/__init__.py
  2. 2 1
      qubes/events.py
  3. 5 0
      qubes/qdb.py
  4. 3 1
      qubes/vm/__init__.py
  5. 6 3
      qubes/vm/appvm.py
  6. 28 3
      qubes/vm/netvm.py
  7. 65 32
      qubes/vm/qubesvm.py
  8. 7 0
      qubes/vm/templatevm.py

+ 18 - 9
qubes/__init__.py

@@ -640,7 +640,7 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
 
         if self._setter is not None:
             value = self._setter(instance, self, value)
-        if self._type is not None:
+        if self._type is not None: # XXX what about QubesVM and other types?
             value = self._type(value)
 
         if has_oldvalue:
@@ -705,7 +705,7 @@ class property(object): # pylint: disable=redefined-builtin,invalid-name
     #
 
     class DontSave(Exception):
-        '''This exception may be raised from saver to sing that property should
+        '''This exception may be raised from saver to sign that property should
         not be saved.
         '''
         pass
@@ -1023,23 +1023,28 @@ class VMProperty(property):
             raise TypeError(
                 "'vmclass' should specify a subclass of qubes.vm.BaseVM")
 
-        super(VMProperty, self).__init__(name, **kwargs)
+        super(VMProperty, self).__init__(name,
+            saver=(lambda self, prop, value: value.name if value else 'None'),
+            **kwargs)
         self.vmclass = vmclass
         self.allow_none = allow_none
 
+
     def __set__(self, instance, value):
         if value is None:
             if self.allow_none:
-                super(VMProperty, self).__set__(self, instance, value)
+                super(VMProperty, self).__set__(instance, value)
                 return
             else:
                 raise ValueError(
                     'Property {!r} does not allow setting to {!r}'.format(
                         self.__name__, value))
 
+        app = instance if isinstance(instance, Qubes) else instance.app
+
         # XXX this may throw LookupError; that's good until introduction
         # of QubesNoSuchVMException or whatever
-        vm = instance.app.domains[value]
+        vm = app.domains[value]
 
         if not isinstance(vm, self.vmclass):
             raise TypeError('wrong VM class: domains[{!r}] if of type {!s} '
@@ -1047,7 +1052,7 @@ class VMProperty(property):
                     vm.__class__.__name__,
                     self.vmclass.__name__))
 
-        super(VMProperty, self).__set__(self, instance, vm)
+        super(VMProperty, self).__set__(instance, vm)
 
 
 import qubes.vm.qubesvm
@@ -1100,11 +1105,11 @@ class Qubes(PropertyHolder):
     '''
 
     default_netvm = VMProperty('default_netvm', load_stage=3,
-        default=None,
+        default=None, allow_none=True,
         doc='''Default NetVM for AppVMs. Initial state is `None`, which means
             that AppVMs are not connected to the Internet.''')
     default_fw_netvm = VMProperty('default_fw_netvm', load_stage=3,
-        default=None,
+        default=None, allow_none=True,
         doc='''Default NetVM for ProxyVMs. Initial state is `None`, which means
             that ProxyVMs (including FirewallVM) are not connected to the
             Internet.''')
@@ -1112,9 +1117,11 @@ class Qubes(PropertyHolder):
         vmclass=qubes.vm.templatevm.TemplateVM,
         doc='Default template for new AppVMs')
     updatevm = VMProperty('updatevm', load_stage=3,
+        allow_none=True,
         doc='''Which VM to use as `yum` proxy for updating AdminVM and
             TemplateVMs''')
     clockvm = VMProperty('clockvm', load_stage=3,
+        allow_none=True,
         doc='Which VM to use as NTP proxy for updating AdminVM')
     default_kernel = property('default_kernel', load_stage=3,
         doc='Which kernel to use when not overriden in VM')
@@ -1209,7 +1216,7 @@ class Qubes(PropertyHolder):
 
         # Disable ntpd in ClockVM - to not conflict with ntpdate (both are
         # using 123/udp port)
-        if hasattr(self, 'clockvm'):
+        if hasattr(self, 'clockvm') and self.clockvm is not None:
             if 'ntpd' in self.clockvm.services:
                 if self.clockvm.services['ntpd']:
                     self.log.warning("VM set as clockvm ({!r}) has enabled "
@@ -1367,6 +1374,8 @@ class Qubes(PropertyHolder):
     @qubes.events.handler('property-pre-set:clockvm')
     def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None):
         # pylint: disable=unused-argument,no-self-use
+        if newvalue is None:
+            return
         if 'ntpd' in newvalue.services:
             if newvalue.services['ntpd']:
                 raise QubesException('Cannot set {!r} as {!r} property since '

+ 2 - 1
qubes/events.py

@@ -102,7 +102,8 @@ class Emitter(object):
 
     def __init__(self, *args, **kwargs):
         super(Emitter, self).__init__(*args, **kwargs)
-        self.events_enabled = False
+        if not hasattr(self, 'events_enabled'):
+            self.events_enabled = False
 
 
     @classmethod

+ 5 - 0
qubes/qdb.py

@@ -0,0 +1,5 @@
+# This is mock file, not installed. It is needed. because pylint needs to
+# import all the modules, and qubes.qbd is one of them.
+
+def QubesDB(dummy):
+    return None

+ 3 - 1
qubes/vm/__init__.py

@@ -353,7 +353,7 @@ class BaseVM(qubes.PropertyHolder):
         args['name'] = self.name
         if hasattr(self, 'kernels_dir'):
             args['kerneldir'] = self.kernels_dir
-        args['uuidnode'] = '<uuid>{!r}</uuid>'.format(self.uuid) \
+        args['uuidnode'] = '<uuid>{!s}</uuid>'.format(self.uuid) \
             if hasattr(self, 'uuid') else ''
         args['vmdir'] = self.dir_path
         args['pcidevs'] = ''.join(lxml.etree.tostring(self.lvxml_pci_dev(dev))
@@ -398,6 +398,8 @@ class BaseVM(qubes.PropertyHolder):
                     "Debug mode: adding 'earlyprintk=xen' to kernel opts")
                 args['kernelopts'] += ' earlyprintk=xen'
 
+        return args
+
 
     def create_config_file(self, file_path=None, prepare_dvm=False):
         '''Create libvirt's XML domain config file

+ 6 - 3
qubes/vm/appvm.py

@@ -1,6 +1,7 @@
 #!/usr/bin/python2 -O
 # vim: fileencoding=utf-8
 
+import qubes.events
 import qubes.vm.qubesvm
 
 class AppVM(qubes.vm.qubesvm.QubesVM):
@@ -11,9 +12,11 @@ class AppVM(qubes.vm.qubesvm.QubesVM):
         ls_width=31,
         doc='Template, on which this AppVM is based.')
 
-    def __init__(self, D):
-        super(AppVM, self).__init__(D)
+    def __init__(self, *args, **kwargs):
+        super(AppVM, self).__init__(*args, **kwargs)
 
+    @qubes.events.handler('domain-loaded')
+    def on_domain_loaded(self, event):
         # Some additional checks for template based VM
         assert self.template
-        self.template.appvms.add(self)
+        #self.template.appvms.add(self) # XXX

+ 28 - 3
qubes/vm/netvm.py

@@ -3,7 +3,32 @@
 
 import qubes.vm.qubesvm
 
-class NetVM(qubes.vm.qubesvm.QubesVM):
+class NetVM(qubes.vm.appvm.AppVM):
     '''Network interface VM'''
-    def __init__(self, D):
-        super(NetVM, self).__init__(D)
+
+    netvm = qubes.property('netvm', setter=qubes.property.forbidden)
+
+    def __init__(self, *args, **kwargs):
+        super(NetVM, self).__init__(*args, **kwargs)
+
+    def get_ip_for_vm(self, vm):
+        return '10.137.{}.{}'.format(self.qid, vm.qid + 2)
+
+    @property
+    def gateway(self):
+        return '10.137.{}.1'.format(self.qid)
+
+    @property
+    def secondary_dns(self):
+        return '10.137.{}.254'.format(self.qid)
+
+#   @property
+#   def netmask(self):
+#       return '255.255.255.0'
+#
+#   @property
+#   def provides_network(self):
+#       return True
+
+    netmask = '255.255.255.0'
+    provides_network = True

+ 65 - 32
qubes/vm/qubesvm.py

@@ -24,6 +24,8 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 #
 
+from __future__ import absolute_import
+
 import datetime
 import lxml.etree
 import os
@@ -34,11 +36,13 @@ import subprocess
 import sys
 import time
 import uuid
+import warnings
+
 import libvirt
 
 import qubes
 import qubes.config
-#import qubes.qdb
+import qubes.qdb
 #import qubes.qmemman
 #import qubes.qmemman_algo
 import qubes.storage
@@ -90,19 +94,29 @@ def _setter_name(self, prop, value):
 
 def _setter_kernel(self, prop, value):
     # pylint: disable=unused-argument
-    if not os.path.exists(os.path.join(
-            qubes.config.system_path['qubes_kernels_base_dir'], value)):
+    dirname = os.path.join(
+        qubes.config.system_path['qubes_base_dir'],
+        qubes.config.system_path['qubes_kernels_base_dir'],
+        value)
+    if not os.path.exists(dirname):
         raise qubes.QubesException('Kernel {!r} not installed'.format(value))
     for filename in ('vmlinuz', 'modules.img'):
-        if not os.path.exists(os.path.join(
-                qubes.config.system_path['qubes_kernels_base_dir'],
-                    value, filename)):
+        if not os.path.exists(os.path.join(dirname, filename)):
             raise qubes.QubesException(
                 'Kernel {!r} not properly installed: missing {!r} file'.format(
                     value, filename))
     return value
 
 
+def _setter_label(self, prop, value):
+    if isinstance(value, qubes.Label):
+        return value
+    if value.startswith('label-'):
+        return self.app.labels[int(value.split('-', 1)[1])]
+
+    return self.app.get_label(value)
+
+
 def _default_conf_file(self, name=None):
     return (name or self.name) + '.conf'
 
@@ -115,7 +129,8 @@ class QubesVM(qubes.vm.BaseVM):
     #
 
     label = qubes.property('label',
-        setter=(lambda self, prop, value: self.app.get_label(value)),
+        setter=_setter_label,
+        saver=(lambda self, prop, value: 'label-{}'.format(value.index)),
         ls_width=14,
         doc='''Colourful label assigned to VM. This is where the colour of the
             padlock is set.''')
@@ -129,9 +144,9 @@ class QubesVM(qubes.vm.BaseVM):
             `None`, machine is disconnected. When absent, domain uses default
             NetVM.''')
 
-    provides_network = qubes.property('provides_network',
-        type=bool, setter=qubes.property.bool,
-        doc='`True` if it is NetVM or ProxyVM, false otherwise.')
+#   provides_network = qubes.property('provides_network',
+#       type=bool, setter=qubes.property.bool,
+#       doc='`True` if it is NetVM or ProxyVM, false otherwise.')
 
     qid = qubes.property('qid', type=int,
         setter=_setter_qid,
@@ -180,8 +195,9 @@ class QubesVM(qubes.vm.BaseVM):
         doc='''Internal VM (not shown in qubes-manager, don't create appmenus
             entries.''')
 
-    # XXX what is that
-    vcpus = qubes.property('vcpus', default=None,
+    # FIXME self.app.host could not exist - only self.app.vm required by API
+    vcpus = qubes.property('vcpus',
+        default=(lambda self: self.app.host.no_cpus),
         ls_width=2,
         doc='FIXME')
 
@@ -215,7 +231,8 @@ class QubesVM(qubes.vm.BaseVM):
     # XXX shouldn't this go to standalone VM and TemplateVM, and leave here
     #     only plain property?
     default_user = qubes.property('default_user', type=str,
-        default=(lambda self: self.template.default_user),
+        default=(lambda self: self.template.default_user
+            if hasattr(self, 'template') else 'user'),
         ls_width=12,
         doc='FIXME')
 
@@ -353,7 +370,7 @@ class QubesVM(qubes.vm.BaseVM):
         If :py:attr:`self.kernel` is :py:obj:`None`, the this points inside
         :py:attr:`self.dir_path`
         '''
-        return os.path.join(
+        return os.path.join(qubes.config.system_path['qubes_base_dir'],
             qubes.config.system_path['qubes_kernels_base_dir'], self.kernel) \
             if self.kernel is not None \
         else os.path.join(self.dir_path,
@@ -379,17 +396,25 @@ class QubesVM(qubes.vm.BaseVM):
 
 
     # XXX I don't know what to do with these; probably should be isinstance(...)
-#   def is_template(self):
-#       return False
-#
-#   def is_appvm(self):
-#       return False
-#
-#   def is_proxyvm(self):
-#       return False
-#
-#   def is_disposablevm(self):
-#       return False
+    def is_template(self):
+        warnings.warn('vm.is_template() is deprecated, use isinstance()',
+            DeprecationWarning)
+        return isinstance(self, qubes.vm.templatevm.TemplateVM)
+
+    def is_appvm(self):
+        warnings.warn('vm.is_appvm() is deprecated, use isinstance()',
+            DeprecationWarning)
+        return isinstance(self, qubes.vm.appvm.AppVM)
+
+    def is_proxyvm(self):
+        warnings.warn('vm.is_proxyvm() is deprecated, use isinstance()',
+            DeprecationWarning)
+        return isinstance(self, qubes.vm.proxyvm.ProxyVM)
+
+    def is_disposablevm(self):
+        warnings.warn('vm.is_disposable() is deprecated, use isinstance()',
+            DeprecationWarning)
+        return isinstance(self, qubes.vm.dispvm.DispVM)
 
 
     # network-related
@@ -399,7 +424,7 @@ class QubesVM(qubes.vm.BaseVM):
     def ip(self):
         '''IP address of this domain.'''
         if self.netvm is not None:
-            return self.netvm.get_ip_for_vm(self.qid)
+            return self.netvm.get_ip_for_vm(self)
         else:
             return None
 
@@ -441,6 +466,13 @@ class QubesVM(qubes.vm.BaseVM):
             return None
         return "vif{0}.+".format(self.xid)
 
+    @property
+    def provides_network(self):
+        ''':py:obj:`True` if it is :py:class:`qubes.vm.netvm.NetVM` or
+        :py:class:`qubes.vm.proxyvm.ProxyVM`, :py:obj:`False` otherwise'''
+        return isinstance(self,
+            (qubes.vm.netvm.NetVM, qubes.vm.proxyvm.ProxyVM))
+
     #
     # constructor
     #
@@ -472,8 +504,8 @@ class QubesVM(qubes.vm.BaseVM):
             self.maxmem = self.memory * 10
 
         # By default allow use all VCPUs
-        if not hasattr(self, 'vcpus') and not self.app.vmm.offline_mode:
-            self.vcpus = self.app.host.no_cpus
+#       if not hasattr(self, 'vcpus') and not self.app.vmm.offline_mode:
+#           self.vcpus = self.app.host.no_cpus
 
         if len(self.devices['pci']) > 0:
             # Force meminfo-writer disabled when VM have PCI devices
@@ -486,10 +518,11 @@ class QubesVM(qubes.vm.BaseVM):
         # Initialize VM image storage class
         self.storage = qubes.storage.get_storage(self)
 
-        if self.kernels_dir is not None: # it is None for AdminVM
-            self.storage.modules_img = os.path.join(self.kernels_dir,
-                'modules.img')
-            self.storage.modules_img_rw = self.kernel is None
+        # XXX should be moved to defaults in storage class
+#       if self.kernels_dir is not None: # it is None for AdminVM
+#           self.storage.modules_img = os.path.join(self.kernels_dir,
+#               'modules.img')
+#           self.storage.modules_img_rw = self.kernel is None
 
         # fire hooks
         self.fire_event('domain-init')

+ 7 - 0
qubes/vm/templatevm.py

@@ -1,6 +1,8 @@
 #!/usr/bin/python2 -O
 # vim: fileencoding=utf-8
 
+import os.path
+
 import qubes
 import qubes.vm.qubesvm
 
@@ -38,3 +40,8 @@ class TemplateVM(qubes.vm.qubesvm.QubesVM):
         self.log.info(
             'Commiting template update; COW: {}'.format(self.rootcow_img))
         self.storage.commit_template_changes()
+
+
+    @property
+    def rootcow_img(self):
+        return os.path.join(self.dir_path, qubes.config.vm_files['rootcow_img'])