diff --git a/core-modules/005QubesNetVm.py b/core-modules/005QubesNetVm.py deleted file mode 100644 index c721bbab..00000000 --- a/core-modules/005QubesNetVm.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/python2 -# -*- coding: utf-8 -*- -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2010 Joanna Rutkowska -# Copyright (C) 2013 Marek Marczykowski -# -# 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) diff --git a/doc/qubes-vm/index.rst b/doc/qubes-vm/index.rst index b136c1dd..65cd8d6e 100644 --- a/doc/qubes-vm/index.rst +++ b/doc/qubes-vm/index.rst @@ -84,8 +84,6 @@ Special VM types: .. toctree:: :maxdepth: 1 - netvm - proxyvm dispvm adminvm diff --git a/doc/qubes-vm/netvm.rst b/doc/qubes-vm/netvm.rst deleted file mode 100644 index 74255edc..00000000 --- a/doc/qubes-vm/netvm.rst +++ /dev/null @@ -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 diff --git a/doc/qubes-vm/proxyvm.rst b/doc/qubes-vm/proxyvm.rst deleted file mode 100644 index 592c94b8..00000000 --- a/doc/qubes-vm/proxyvm.rst +++ /dev/null @@ -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 diff --git a/qubes/vm/__init__.py b/qubes/vm/__init__.py index 2d91526d..fd2cd448 100644 --- a/qubes/vm/__init__.py +++ b/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)) diff --git a/qubes/vm/mix/__init__.py b/qubes/vm/mix/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/qubes/vm/mix/net.py b/qubes/vm/mix/net.py new file mode 100644 index 00000000..7327543d --- /dev/null +++ b/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 +# Copyright (C) 2013-2016 Marek Marczykowski-Górecki +# +# Copyright (C) 2014-2016 Wojtek Porczyk +# +# 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. + # + + 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) + + diff --git a/qubes/vm/netvm.py b/qubes/vm/netvm.py deleted file mode 100644 index 90ac57e3..00000000 --- a/qubes/vm/netvm.py +++ /dev/null @@ -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 diff --git a/qubes/vm/proxyvm.py b/qubes/vm/proxyvm.py deleted file mode 100644 index 4c872014..00000000 --- a/qubes/vm/proxyvm.py +++ /dev/null @@ -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) diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index 70be7495..539c8f32 100644 --- a/qubes/vm/qubesvm.py +++ b/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 # diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 0f1095c5..c2347c75 100644 --- a/rpm_spec/core-dom0.spec +++ b/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*