Forráskód Böngészése

qubes: port netvm

From now, there are no separate NetVM and ProxyVM class, but property
"provides_network".
Wojtek Porczyk 8 éve
szülő
commit
01319e391f

+ 0 - 168
core-modules/005QubesNetVm.py

@@ -1,168 +0,0 @@
-#!/usr/bin/python2
-# -*- coding: utf-8 -*-
-#
-# The Qubes OS Project, http://www.qubes-os.org
-#
-# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
-# Copyright (C) 2013  Marek Marczykowski <marmarek@invisiblethingslab.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-#
-import sys
-import os.path
-import xen.lowlevel.xs
-
-from qubes.qubes import QubesVm,register_qubes_vm_class,vmm,dry_run
-from qubes.qubes import defaults,system_path,vm_files
-from qubes.qubes import QubesVmCollection,QubesException
-
-class QubesNetVm(QubesVm):
-    """
-    A class that represents a NetVM. A child of QubesCowVM.
-    """
-
-    # In which order load this VM type from qubes.xml
-    load_order = 70
-
-    def get_attrs_config(self):
-        attrs_config = super(QubesNetVm, self).get_attrs_config()
-        attrs_config['dir_path']['func'] = \
-            lambda value: value if value is not None else \
-                os.path.join(system_path["qubes_servicevms_dir"], self.name)
-        attrs_config['label']['default'] = defaults["servicevm_label"]
-        attrs_config['memory']['default'] = 300
-
-        # New attributes
-        attrs_config['netid'] = {
-            'save': lambda: str(self.netid),
-            'order': 30,
-            'func': lambda value: value if value is not None else
-            self._collection.get_new_unused_netid() }
-        attrs_config['netprefix'] = {
-            'func': lambda x: "10.137.{0}.".format(self.netid) }
-        attrs_config['dispnetprefix'] = {
-            'func': lambda x: "10.138.{0}.".format(self.netid) }
-
-        # Dont save netvm prop
-        attrs_config['netvm'].pop('save')
-        attrs_config['uses_default_netvm'].pop('save')
-
-        return attrs_config
-
-    def __init__(self, **kwargs):
-        super(QubesNetVm, self).__init__(**kwargs)
-        self.connected_vms = QubesVmCollection()
-
-        self.__network = "10.137.{0}.0".format(self.netid)
-        self.__netmask = defaults["vm_default_netmask"]
-        self.__gateway = self.netprefix + "1"
-        self.__secondary_dns = self.netprefix + "254"
-
-        self.__external_ip_allowed_xids = set()
-
-        self.log.debug('network={} netmask={} gateway={} secondary_dns={}'.format(
-            self.network, self.netmask, self.gateway, self.secondary_dns))
-
-    @property
-    def type(self):
-        return "NetVM"
-
-    def is_netvm(self):
-        return True
-
-    @property
-    def gateway(self):
-        return self.__gateway
-
-    @property
-    def secondary_dns(self):
-        return self.__secondary_dns
-
-    @property
-    def netmask(self):
-        return self.__netmask
-
-    @property
-    def network(self):
-        return self.__network
-
-    def get_ip_for_vm(self, qid):
-        lo = qid % 253 + 2
-        assert lo >= 2 and lo <= 254, "Wrong IP address for VM"
-        return self.netprefix  + "{0}".format(lo)
-
-    def get_ip_for_dispvm(self, dispid):
-        lo = dispid % 254 + 1
-        assert lo >= 1 and lo <= 254, "Wrong IP address for VM"
-        return self.dispnetprefix  + "{0}".format(lo)
-
-    def update_external_ip_permissions(self, xid = -1):
-        # TODO: VMs in __external_ip_allowed_xids should be notified via RPC
-        # service on exteran IP change
-        pass
-
-    def start(self, **kwargs):
-        if dry_run:
-            return
-
-        xid=super(QubesNetVm, self).start(**kwargs)
-
-        # Connect vif's of already running VMs
-        for vm in self.connected_vms.values():
-            if not vm.is_running():
-                continue
-
-            if 'verbose' in kwargs and kwargs['verbose']:
-                print >> sys.stderr, "--> Attaching network to '{0}'...".format(vm.name)
-
-            # Cleanup stale VIFs
-            vm.cleanup_vifs()
-
-            # force frontend to forget about this device
-            #  module actually will be loaded back by udev, as soon as network is attached
-            try:
-                vm.run("modprobe -r xen-netfront xennet", user="root")
-            except:
-                pass
-
-            try:
-                vm.attach_network(wait=False)
-            except QubesException as ex:
-                print >> sys.stderr, ("WARNING: Cannot attach to network to '{0}': {1}".format(vm.name, ex))
-
-        return xid
-
-    def shutdown(self, force=False):
-        if dry_run:
-            return
-
-        connected_vms =  [vm for vm in self.connected_vms.values() if vm.is_running()]
-        if connected_vms and not force:
-            raise QubesException("There are other VMs connected to this VM: " + str([vm.name for vm in connected_vms]))
-
-        super(QubesNetVm, self).shutdown(force=force)
-
-    def add_external_ip_permission(self, xid):
-        if int(xid) < 0:
-            return
-        self.__external_ip_allowed_xids.add(int(xid))
-        self.update_external_ip_permissions()
-
-    def remove_external_ip_permission(self, xid):
-        self.__external_ip_allowed_xids.discard(int(xid))
-        self.update_external_ip_permissions()
-
-register_qubes_vm_class(QubesNetVm)

+ 0 - 2
doc/qubes-vm/index.rst

@@ -84,8 +84,6 @@ Special VM types:
 .. toctree::
    :maxdepth: 1
 
-   netvm
-   proxyvm
    dispvm
    adminvm
 

+ 0 - 8
doc/qubes-vm/netvm.rst

@@ -1,8 +0,0 @@
-:py:mod:`qubes.vm.netvm` -- Network interface VM
-================================================
-
-.. automodule:: qubes.vm.netvm
-   :members:
-   :show-inheritance:
-
-.. vim: ts=3 sw=3 et

+ 0 - 8
doc/qubes-vm/proxyvm.rst

@@ -1,8 +0,0 @@
-:py:mod:`qubes.vm.proxyvm` -- Proxy (firewall/VPN) VM
-=====================================================
-
-.. automodule:: qubes.vm.proxyvm
-   :members:
-   :show-inheritance:
-
-.. vim: ts=3 sw=3 et

+ 5 - 2
qubes/vm/__init__.py

@@ -32,6 +32,7 @@ import ast
 import collections
 import datetime
 import functools
+import itertools
 import os
 import re
 import subprocess
@@ -332,8 +333,10 @@ class BaseVM(qubes.PropertyHolder):
             args['ip'] = self.ip
             args['mac'] = self.mac
             args['gateway'] = self.netvm.gateway
-            args['dns1'] = self.netvm.gateway
-            args['dns2'] = self.secondary_dns
+
+            for i, addr in zip(itertools.count(start=1), self.dns):
+                args['dns{}'.format(i)] = addr
+
             args['netmask'] = self.netmask
             args['netdev'] = lxml.etree.tostring(
                 self.lvxml_net_dev(self.ip, self.mac, self.netvm))

+ 0 - 0
qubes/vm/mix/__init__.py


+ 297 - 0
qubes/vm/mix/net.py

@@ -0,0 +1,297 @@
+#!/usr/bin/python2 -O
+# vim: fileencoding=utf-8
+
+#
+# The Qubes OS Project, https://www.qubes-os.org/
+#
+# Copyright (C) 2010-2016  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2013-2016  Marek Marczykowski-Górecki
+#                              <marmarek@invisiblethingslab.com>
+# Copyright (C) 2014-2016  Wojtek Porczyk <woju@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+import weakref
+
+import qubes
+
+class NetVMMixin(object):
+    mac = qubes.property('mac', type=str,
+        default=(lambda self: '00:16:3E:5E:6C:{:02X}'.format(self.qid)),
+        ls_width=17,
+        doc='MAC address of the NIC emulated inside VM')
+
+    # XXX swallowed uses_default_netvm
+    netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True,
+        default=(lambda self: self.app.default_fw_netvm if self.provides_network
+            else self.app.default_netvm),
+        ls_width=31,
+        doc='''VM that provides network connection to this domain. When
+            `None`, machine is disconnected. When absent, domain uses default
+            NetVM.''')
+
+    provides_network = qubes.property('provides_network', default=False,
+        type=bool, setter=qubes.property.bool,
+        doc='''If this domain can act as network provider (formerly known as
+            NetVM or ProxyVM)''')
+
+
+    #
+    # used in networked appvms or proxyvms (netvm is not None)
+    #
+
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def ip(self):
+        '''IP address of this domain.'''
+        if not self.is_networked():
+            return None
+        if self.netvm is not None:
+            return self.netvm.get_ip_for_vm(self)
+        else:
+            return self.get_ip_for_vm(self)
+
+
+    #
+    # used in netvms (provides_network=True)
+    # those properties and methods are most likely accessed as vm.netvm.<prop>
+    #
+
+    def get_ip_for_vm(self, vm):
+        '''Get IP address for (appvm) domain connected to this (netvm) domain.
+        '''
+        import qubes.vm.dispvm
+        if isinstance(vm, qubes.vm.dispvm.DispVM):
+            return '10.138.{}.{}'.format((vm.dispid >> 8) & 7, vm.dispid & 7)
+
+        # VM technically can get address which ends in '.0'. This currently
+        # does not happen, because qid < 253, but may happen in the future.
+        return '10.137.{}.{}'.format((vm.qid >> 8) & 7, vm.qid & 7)
+
+    @qubes.tools.qvm_ls.column(head='IPBACK', width=15)
+    @property
+    def gateway(self):
+        '''Gateway for other domains that use this domain as netvm.'''
+        return self.ip if self.provides_network else None
+
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def netmask(self):
+        '''Netmask for gateway address.'''
+        return '255.255.255.255' if self.is_networked() else None
+
+    @qubes.tools.qvm_ls.column(width=7)
+    @property
+    def vif(self):
+        '''Name of the network interface backend in netvm that is connected to
+        NIC inside this domain.'''
+        if self.xid < 0:
+            return None
+        if self.netvm is None:
+            return None
+        return "vif{0}.+".format(self.xid)
+
+
+    #
+    # used in both
+    #
+
+    @qubes.tools.qvm_ls.column(width=15)
+    @property
+    def dns(self):
+        '''Secondary DNS server set up for this domain.'''
+        if self.netvm is not None or self.provides_network:
+            return (
+                '10.139.1.1',
+                '10.139.1.2',
+            )
+        else:
+            return None
+
+
+    def __init__(self, *args, **kwargs):
+        super(NetVMMixin, self).__init__(*args, **kwargs)
+        self.connected_vms = weakref.WeakSet()
+
+
+    @qubes.events.handler('domain-started')
+    def start_net(self):
+        '''Connect this domain to its downstream domains.
+
+        This is needed when starting netvm *after* its connected domains.
+        '''
+
+        for vm in self.connected_vms:
+            if not vm.is_running():
+                continue
+            vm.log.info('Attaching network')
+            # 1426
+            vm.cleanup_vifs()
+
+            try:
+                # 1426
+                vm.run('modprobe -r xen-netfront xennet',
+                    user='root', wait=True)
+            except:
+                pass
+
+            try:
+                vm.attach_network(wait=False)
+            except QubesException as e:
+                vm.log.warning('Cannot attach network', exc_info=1)
+
+
+    @qubes.events.handler('pre-domain-shutdown')
+    def shutdown_net(self, force=False):
+        connected_vms = [vm for vm in self.connected_vms.values() if vm.is_running()]
+        if connected_vms and not force:
+            raise qubes.exc.QubesVMError(
+                'There are other VMs connected to this VM: {}'.format(
+                    ', '.join(vm.name for vm in connected_vms)))
+
+        # detach network interfaces of connected VMs before shutting down,
+        # otherwise libvirt will not notice it and will try to detach them
+        # again (which would fail, obviously).
+        # This code can be removed when #1426 got implemented
+        for vm in connected_vms:
+            if vm.is_running():
+                try:
+                    vm.detach_network()
+                except (QubesException, libvirt.libvirtError):
+                    # ignore errors
+                    pass
+
+
+    # TODO maybe this should be other way: backend.devices['net'].attach(self)
+    def attach_network(self):
+        '''Attach network in this machine to it's netvm.'''
+
+        if not self.is_running():
+            raise qubes.exc.QubesVMNotRunningError(self)
+        assert self.netvm is not None
+
+        if not self.netvm.is_running():
+            self.log.info('Starting NetVM ({0})'.format(self.netvm.name))
+            self.netvm.start()
+
+        self.libvirt_domain.attachDevice(lxml.etree.ElementTree(
+            self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring())
+
+
+    def detach_network(self):
+        '''Detach machine from it's netvm'''
+
+        if not self.is_running():
+            raise qubes.exc.QubesVMNotRunningError(self)
+        assert self.netvm is not None
+
+        self.libvirt_domain.detachDevice(lxml.etree.ElementTree(
+            self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring())
+
+
+    def is_networked(self):
+        '''Check whether this VM can reach network (firewall notwithstanding).
+
+        :returns: :py:obj:`True` if is machine can reach network, \
+            :py:obj:`False` otherwise.
+        :rtype: bool
+        '''
+
+        if self.provides_network:
+            return True
+
+        return self.netvm is not None
+
+
+    def cleanup_vifs(self):
+        '''Remove stale network device backends.
+
+        Libvirt does not remove vif when backend domain is down, so we must do
+        it manually. This method is one big hack for #1426.
+        '''
+
+        # FIXME: remove this?
+        if not self.is_running():
+            return
+
+        dev_basepath = '/local/domain/%d/device/vif' % self.xid
+        for dev in self.app.vmm.xs.ls('', dev_basepath):
+            # check if backend domain is alive
+            backend_xid = int(self.app.vmm.xs.read('',
+                '{}/{}/backend-id'.format(dev_basepath, dev)))
+            if backend_xid in self.app.vmm.libvirt_conn.listDomainsID():
+                # check if device is still active
+                if self.app.vmm.xs.read('',
+                        '{}/{}/state'.format(dev_basepath, dev)) == '4':
+                    continue
+            # remove dead device
+            self.app.vmm.xs.rm('', '{}/{}'.format(dev_basepath, dev))
+
+
+    @qubes.events.handler('property-del:netvm')
+    def on_property_del_netvm(self, event, name, old_netvm):
+        # pylint: disable=unused-argument
+        # we are changing to default netvm
+        new_netvm = self.netvm
+        if new_netvm == old_netvm:
+            return
+        self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm)
+
+
+    @qubes.events.handler('property-set:netvm')
+    def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None):
+        # pylint: disable=unused-argument
+        if self.is_running() and new_netvm is not None \
+                and not new_netvm.is_running():
+            raise qubes.exc.QubesVMNotStartedError(new_netvm,
+                'Cannot dynamically attach to stopped NetVM: {!r}'.format(
+                    new_netvm))
+
+        if self.netvm is not None:
+            self.netvm.connected_vms.remove(self)
+            if self.is_running():
+                self.detach_network()
+
+                # TODO change to domain-removed event handler in netvm
+#               if hasattr(self.netvm, 'post_vm_net_detach'):
+#                   self.netvm.post_vm_net_detach(self)
+
+        if new_netvm is None:
+#           if not self._do_not_reset_firewall:
+            # Set also firewall to block all traffic as discussed in #370
+            if os.path.exists(self.firewall_conf):
+                shutil.copy(self.firewall_conf,
+                    os.path.join(qubes.config.system_path['qubes_base_dir'],
+                        'backup',
+                        '%s-firewall-%s.xml' % (self.name,
+                            time.strftime('%Y-%m-%d-%H:%M:%S'))))
+            self.write_firewall_conf({'allow': False, 'allowDns': False,
+                'allowIcmp': False, 'allowYumProxy': False, 'rules': []})
+        else:
+            new_netvm.connected_vms.add(self)
+
+        if new_netvm is None:
+            return
+
+        if self.is_running():
+            # refresh IP, DNS etc
+            self.create_qdb_entries()
+            self.attach_network()
+
+            # TODO documentation
+            new_netvm.fire_event('net-domain-connected', self)
+
+

+ 0 - 34
qubes/vm/netvm.py

@@ -1,34 +0,0 @@
-#!/usr/bin/python2 -O
-# vim: fileencoding=utf-8
-
-import qubes.vm.qubesvm
-
-class NetVM(qubes.vm.appvm.AppVM):
-    '''Network interface VM'''
-
-    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

+ 0 - 9
qubes/vm/proxyvm.py

@@ -1,9 +0,0 @@
-#!/usr/bin/python2 -O
-# vim: fileencoding=utf-8
-
-import qubes.vm.netvm
-
-class ProxyVM(qubes.vm.netvm.NetVM):
-    '''Proxy (firewall/VPN) VM'''
-    def __init__(self, D):
-        super(ProxyVM, self).__init__(D)

+ 38 - 221
qubes/vm/qubesvm.py

@@ -27,7 +27,7 @@
 from __future__ import absolute_import
 
 import datetime
-import lxml.etree
+import itertools
 import os
 import os.path
 import re
@@ -39,6 +39,7 @@ import uuid
 import warnings
 
 import libvirt
+import lxml.etree
 
 import qubes
 import qubes.config
@@ -46,6 +47,7 @@ import qubes.exc
 import qubes.storage
 import qubes.utils
 import qubes.vm
+import qubes.vm.mix.net
 import qubes.tools.qvm_ls
 
 qmemman_present = False
@@ -121,7 +123,7 @@ def _default_conf_file(self, name=None):
     return (name or self.name) + '.conf'
 
 
-class QubesVM(qubes.vm.BaseVM):
+class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
     '''Base functionality of Qubes VM shared between all VMs.'''
 
     #
@@ -142,15 +144,6 @@ class QubesVM(qubes.vm.BaseVM):
         doc='''Colourful label assigned to VM. This is where the colour of the
             padlock is set.''')
 
-    # XXX swallowed uses_default_netvm
-    netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True,
-        default=(lambda self: self.app.default_fw_netvm if self.provides_network
-            else self.app.default_netvm),
-        ls_width=31,
-        doc='''VM that provides network connection to this domain. When
-            `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.')
@@ -220,11 +213,6 @@ class QubesVM(qubes.vm.BaseVM):
         ls_width=30,
         doc='Kernel command line passed to domain.')
 
-    mac = qubes.property('mac', type=str,
-        default=(lambda self: '00:16:3E:5E:6C:{:02X}'.format(self.qid)),
-        ls_width=17,
-        doc='MAC address of the NIC emulated inside VM')
-
     debug = qubes.property('debug', type=bool, default=False,
         setter=qubes.property.bool,
         doc='Turns on debugging features.')
@@ -413,59 +401,6 @@ class QubesVM(qubes.vm.BaseVM):
 
     # network-related
 
-    @qubes.tools.qvm_ls.column(width=15)
-    @property
-    def ip(self):
-        '''IP address of this domain.'''
-        if self.netvm is not None:
-            return self.netvm.get_ip_for_vm(self)
-        else:
-            return None
-
-    @qubes.tools.qvm_ls.column(width=15)
-    @property
-    def netmask(self):
-        '''Netmask for this domain's IP address.'''
-        if self.netvm is not None:
-            return self.netvm.netmask
-        else:
-            return None
-
-    @qubes.tools.qvm_ls.column(head='IPBACK', width=15)
-    @property
-    def gateway(self):
-        '''Gateway for other domains that use this domain as netvm.'''
-        # pylint: disable=no-self-use
-
-        # This is gateway IP for _other_ VMs, so make sense only in NetVMs
-        return None
-
-    @qubes.tools.qvm_ls.column(width=15)
-    @property
-    def secondary_dns(self):
-        '''Secondary DNS server set up for this domain.'''
-        if self.netvm is not None:
-            return self.netvm.secondary_dns
-        else:
-            return None
-
-    @qubes.tools.qvm_ls.column(width=7)
-    @property
-    def vif(self):
-        '''Name of the network interface backend in netvm that is connected to
-        NIC inside this domain.'''
-        if self.xid < 0:
-            return None
-        if self.netvm is None:
-            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
@@ -545,61 +480,6 @@ class QubesVM(qubes.vm.BaseVM):
                 shutil.copy(new_label.icon_path, self.icon_path)
 
 
-    @qubes.events.handler('property-del:netvm')
-    def on_property_del_netvm(self, event, name, old_netvm):
-        # pylint: disable=unused-argument
-        # we are changing to default netvm
-        new_netvm = self.netvm
-        if new_netvm == old_netvm:
-            return
-        self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm)
-
-
-    @qubes.events.handler('property-set:netvm')
-    def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None):
-        # pylint: disable=unused-argument
-        if self.is_running() and new_netvm is not None \
-                and not new_netvm.is_running():
-            raise qubes.exc.QubesVMNotStartedError(new_netvm,
-                'Cannot dynamically attach to stopped NetVM: {!r}'.format(
-                    new_netvm))
-
-        if self.netvm is not None:
-            del self.netvm.connected_vms[self]
-            if self.is_running():
-                self.detach_network()
-
-                # TODO change to domain-removed event handler in netvm
-#               if hasattr(self.netvm, 'post_vm_net_detach'):
-#                   self.netvm.post_vm_net_detach(self)
-
-        if new_netvm is None:
-#           if not self._do_not_reset_firewall:
-            # Set also firewall to block all traffic as discussed in #370
-            if os.path.exists(self.firewall_conf):
-                shutil.copy(self.firewall_conf,
-                    os.path.join(qubes.config.system_path['qubes_base_dir'],
-                        'backup',
-                        '%s-firewall-%s.xml' % (self.name,
-                            time.strftime('%Y-%m-%d-%H:%M:%S'))))
-            self.write_firewall_conf({'allow': False, 'allowDns': False,
-                'allowIcmp': False, 'allowYumProxy': False, 'rules': []})
-        else:
-            new_netvm.connected_vms.add(self)
-
-        if new_netvm is None:
-            return
-
-        if self.is_running():
-            # refresh IP, DNS etc
-            self.create_qdb_entries()
-            self.attach_network()
-
-            # TODO domain-added event handler in netvm
-#           if hasattr(self.netvm, 'post_vm_net_attach'):
-#               self.netvm.post_vm_net_attach(self)
-
-
     @qubes.events.handler('property-pre-set:name')
     def on_property_pre_set_name(self, event, name, newvalue, oldvalue=None):
         # pylint: disable=unused-argument
@@ -768,10 +648,6 @@ class QubesVM(qubes.vm.BaseVM):
             if vm.is_proxyvm() and vm.is_running():
                 vm.write_iptables_xenstore_entry()
 
-        self.fire_event('domain-started',
-            preparing_dvm=preparing_dvm, start_guid=start_guid)
-
-
         self.log.warning('Activating the {} VM'.format(self.name))
         self.libvirt_domain.resume()
 
@@ -794,8 +670,11 @@ class QubesVM(qubes.vm.BaseVM):
                 and os.path.exists('/var/run/shm.id'):
             self.start_guid()
 
+        self.fire_event('domain-started',
+            preparing_dvm=preparing_dvm, start_guid=start_guid)
+
 
-    def shutdown(self):
+    def shutdown(self, force=False):
         '''Shutdown domain.
 
         :raises qubes.exc.QubesVMNotStartedError: \
@@ -805,10 +684,11 @@ class QubesVM(qubes.vm.BaseVM):
         if not self.is_running(): # TODO not self.is_halted()
             raise qubes.exc.QubesVMNotStartedError(self)
 
+        self.fire_event_pre('pre-domain-shutdown', force=force)
         self.libvirt_domain.shutdown()
 
 
-    def force_shutdown(self):
+    def kill(self):
         '''Forcefuly shutdown (destroy) domain.
 
         :raises qubes.exc.QubesVMNotStartedError: \
@@ -821,6 +701,14 @@ class QubesVM(qubes.vm.BaseVM):
         self.libvirt_domain.destroy()
 
 
+    def force_shutdown(self, *args, **kwargs):
+        '''Deprecated alias for :py:meth:`kill`'''
+        warnings.warn(
+            'Call to deprecated function force_shutdown(), use kill() instead',
+            DeprecationWarning, stacklevel=2)
+        self.kill(*args, **kwargs)
+
+
     def suspend(self):
         '''Suspend (pause) domain.
 
@@ -1180,33 +1068,6 @@ class QubesVM(qubes.vm.BaseVM):
         self.fire_event('cloned-files', src)
 
 
-    # TODO maybe this should be other way: backend.devices['net'].attach(self)
-    def attach_network(self):
-        '''Attach network in this machine to it's netvm.'''
-
-        if not self.is_running():
-            raise qubes.exc.QubesVMNotRunningError(self)
-        assert self.netvm is not None
-
-        if not self.netvm.is_running():
-            self.log.info('Starting NetVM ({0})'.format(self.netvm.name))
-            self.netvm.start()
-
-        self.libvirt_domain.attachDevice(lxml.etree.ElementTree(
-            self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring())
-
-
-    def detach_network(self):
-        '''Detach machine from it's netvm'''
-
-        if not self.is_running():
-            raise qubes.exc.QubesVMNotRunningError(self)
-        assert self.netvm is not None
-
-        self.libvirt_domain.detachDevice(lxml.etree.ElementTree(
-            self.lvxml_net_dev(self.ip, self.mac, self.netvm)).tostring())
-
-
     #
     # methods for querying domain state
     #
@@ -1619,20 +1480,6 @@ class QubesVM(qubes.vm.BaseVM):
         return used_dmdev != current_dmdev
 
 
-    def is_networked(self):
-        '''Check whether this VM can reach network (firewall notwithstanding).
-
-        :returns: :py:obj:`True` if is machine can reach network, \
-            :py:obj:`False` otherwise.
-        :rtype: bool
-        '''
-
-        if self.provides_network:
-            return True
-
-        return self.netvm is not None
-
-
     #
     # helper methods
     #
@@ -1652,36 +1499,40 @@ class QubesVM(qubes.vm.BaseVM):
         '''
         # pylint: disable=no-member
 
-        self.qdb.write("/name", self.name)
-        self.qdb.write("/qubes-vm-type", self.__class__.__name__)
-        self.qdb.write("/qubes-vm-updateable", str(self.updateable))
+        self.qdb.write('/name', self.name)
+        self.qdb.write('/qubes-vm-type', self.__class__.__name__)
+        self.qdb.write('/qubes-vm-updateable', str(self.updateable))
 
         if self.provides_network:
-            self.qdb.write("/qubes-netvm-gateway", self.gateway)
-            self.qdb.write("/qubes-netvm-secondary-dns", self.secondary_dns)
-            self.qdb.write("/qubes-netvm-netmask", self.netmask)
-            self.qdb.write("/qubes-netvm-network", self.network)
+            self.qdb.write('/network-provider/gateway', self.gateway)
+            self.qdb.write('/network-provider/netmask', self.netmask)
+
+            for i, addr in zip(itertools.count(start=1), self.dns):
+                self.qdb.write('/network-provider/dns-{}'.format(i), addr)
 
         if self.netvm is not None:
-            self.qdb.write("/qubes-ip", self.ip)
-            self.qdb.write("/qubes-netmask", self.netvm.netmask)
-            self.qdb.write("/qubes-gateway", self.netvm.gateway)
-            self.qdb.write("/qubes-secondary-dns", self.netvm.secondary_dns)
+            self.qdb.write('/network/ip', self.ip)
+            self.qdb.write('/network/netmask', self.netvm.netmask)
+            self.qdb.write('/network/gateway', self.netvm.gateway)
+
+            for i, addr in zip(itertools.count(start=1), self.dns):
+                self.qdb.write('/network/dns-{}'.format(i), addr)
+
 
         tzname = qubes.utils.get_timezone()
         if tzname:
-            self.qdb.write("/qubes-timezone", tzname)
+            self.qdb.write('/qubes-timezone', tzname)
 
         for srv in self.services.keys():
             # convert True/False to "1"/"0"
-            self.qdb.write("/qubes-service/{0}".format(srv),
+            self.qdb.write('/qubes-service/{0}'.format(srv),
                     str(int(self.services[srv])))
 
-        self.qdb.write("/qubes-block-devices", '')
+        self.qdb.write('/qubes-block-devices', '')
 
-        self.qdb.write("/qubes-usb-devices", '')
+        self.qdb.write('/qubes-usb-devices', '')
 
-        self.qdb.write("/qubes-debug-mode", str(int(self.debug)))
+        self.qdb.write('/qubes-debug-mode', str(int(self.debug)))
 
         # TODO: Currently the whole qmemman is quite Xen-specific, so stay with
         # xenstore for it until decided otherwise
@@ -1711,40 +1562,6 @@ class QubesVM(qubes.vm.BaseVM):
                 raise
 
 
-    def cleanup_vifs(self):
-        '''Remove stale network device backends.
-
-        Xend does not remove vif when backend domain is down, so we must do it
-        manually.
-        '''
-
-        # FIXME: remove this?
-        if not self.is_running():
-            return
-
-        dev_basepath = '/local/domain/%d/device/vif' % self.xid
-        for dev in self.app.vmm.xs.ls('', dev_basepath):
-            # check if backend domain is alive
-            backend_xid = int(self.app.vmm.xs.read('',
-                '{}/{}/backend-id'.format(dev_basepath, dev)))
-            if backend_xid in self.app.vmm.libvirt_conn.listDomainsID():
-                # check if device is still active
-                if self.app.vmm.xs.read('',
-                        '{}/{}/state'.format(dev_basepath, dev)) == '4':
-                    continue
-            # remove dead device
-            self.app.vmm.xs.rm('', '{}/{}'.format(dev_basepath, dev))
-
-
-
-
-
-
-
-
-
-
-
     #
     # workshop -- those are to be reworked later
     #

+ 4 - 2
rpm_spec/core-dom0.spec

@@ -218,12 +218,14 @@ fi
 %{python_sitelib}/qubes/vm/appvm.py*
 %{python_sitelib}/qubes/vm/dispvm.py*
 %{python_sitelib}/qubes/vm/hvm.py*
-%{python_sitelib}/qubes/vm/netvm.py*
-%{python_sitelib}/qubes/vm/proxyvm.py*
 %{python_sitelib}/qubes/vm/qubesvm.py*
 %{python_sitelib}/qubes/vm/templatehvm.py*
 %{python_sitelib}/qubes/vm/templatevm.py*
 
+%dir %{python_sitelib}/qubes/vm/mix
+%{python_sitelib}/qubes/vm/mix/__init__.py*
+%{python_sitelib}/qubes/vm/mix/net.py*
+
 %dir %{python_sitelib}/qubes/storage
 %{python_sitelib}/qubes/storage/__init__.py*
 %{python_sitelib}/qubes/storage/xen.py*