Merge remote-tracking branch 'woju/pull/18/head' into core3-devel

woju/qubes-core-admin#18
This commit is contained in:
Wojtek Porczyk 2016-06-16 21:09:59 +02:00
commit 8f7732e07d
6 changed files with 136 additions and 126 deletions

View File

@ -28,13 +28,8 @@
''' '''
import ast
import collections
import datetime import datetime
import functools
import itertools
import os import os
import re
import subprocess import subprocess
import sys import sys
import xml.parsers.expat import xml.parsers.expat
@ -125,7 +120,9 @@ class Features(dict):
# #
_NO_DEFAULT = object() _NO_DEFAULT = object()
def check_with_template(self, feature, default=_NO_DEFAULT): def check_with_template(self, feature, default=_NO_DEFAULT):
''' Check if the vm's template has the specified feature. '''
if feature in self: if feature in self:
return self[feature] return self[feature]
@ -198,14 +195,14 @@ class BaseVM(qubes.PropertyHolder):
for node in xml.xpath('./tags/tag'): for node in xml.xpath('./tags/tag'):
self.tags[node.get('name')] = node.text self.tags[node.get('name')] = node.text
# TODO: firewall, policy # SEE:1815 firewall, policy.
# check if properties are appropriate # check if properties are appropriate
all_names = set(prop.__name__ for prop in self.property_list()) all_names = set(prop.__name__ for prop in self.property_list())
for node in self.xml.xpath('./properties/property'): for node in self.xml.xpath('./properties/property'):
name = node.get('name') name = node.get('name')
if not name in all_names: if name not in all_names:
raise TypeError( raise TypeError(
'property {!r} not applicable to {!r}'.format( 'property {!r} not applicable to {!r}'.format(
name, self.__class__.__name__)) name, self.__class__.__name__))
@ -216,12 +213,10 @@ class BaseVM(qubes.PropertyHolder):
if hasattr(self, 'name'): if hasattr(self, 'name'):
self.init_log() self.init_log()
def init_log(self): def init_log(self):
'''Initialise logger for this domain.''' '''Initialise logger for this domain.'''
self.log = qubes.log.get_vm_logger(self.name) self.log = qubes.log.get_vm_logger(self.name)
def __xml__(self): def __xml__(self):
element = lxml.etree.Element('domain') element = lxml.etree.Element('domain')
element.set('id', 'domain-' + str(self.qid)) element.set('id', 'domain-' + str(self.qid))
@ -266,54 +261,24 @@ class BaseVM(qubes.PropertyHolder):
return '<{} object at {:#x} {}>'.format( return '<{} object at {:#x} {}>'.format(
self.__class__.__name__, id(self), ' '.join(proprepr)) self.__class__.__name__, id(self), ' '.join(proprepr))
# #
# xml serialising methods # xml serialising methods
# #
def create_config_file(self, file_path=None, prepare_dvm=False): def create_config_file(self, prepare_dvm=False):
'''Create libvirt's XML domain config file '''Create libvirt's XML domain config file
If :py:attr:`qubes.vm.qubesvm.QubesVM.uses_custom_config` is true, this
does nothing.
:param str file_path: Path to file to create \
(default: :py:attr:`qubes.vm.qubesvm.QubesVM.conf_file`)
:param bool prepare_dvm: If we are in the process of preparing \ :param bool prepare_dvm: If we are in the process of preparing \
DisposableVM DisposableVM
''' '''
if file_path is None:
file_path = self.conf_file
# TODO
# if self.uses_custom_config:
# conf_appvm = open(file_path, "r")
# domain_config = conf_appvm.read()
# conf_appvm.close()
# return domain_config
domain_config = self.app.env.get_template('libvirt/xen.xml').render( domain_config = self.app.env.get_template('libvirt/xen.xml').render(
vm=self, prepare_dvm=prepare_dvm) vm=self, prepare_dvm=prepare_dvm)
# FIXME: This is only for debugging purposes
old_umask = os.umask(002)
try:
conf_appvm = open(file_path, "w")
conf_appvm.write(domain_config)
conf_appvm.close()
except: # pylint: disable=bare-except
# Ignore errors
pass
finally:
os.umask(old_umask)
return domain_config return domain_config
# #
# firewall # firewall
# TODO rewrite it, have <firewall/> node under <domain/> # SEE:1815 rewrite it, have <firewall/> node under <domain/>
# and possibly integrate with generic policy framework # and possibly integrate with generic policy framework.
# #
def write_firewall_conf(self, conf): def write_firewall_conf(self, conf):
@ -366,7 +331,7 @@ class BaseVM(qubes.PropertyHolder):
tree.write(fd, encoding="UTF-8", pretty_print=True) tree.write(fd, encoding="UTF-8", pretty_print=True)
fd.close() fd.close()
os.umask(old_umask) os.umask(old_umask)
except EnvironmentError as err: # pylint: disable=broad-except except EnvironmentError as err: # pylint: disable=broad-except
print >> sys.stderr, "{0}: save error: {1}".format( print >> sys.stderr, "{0}: save error: {1}".format(
os.path.basename(sys.argv[0]), err) os.path.basename(sys.argv[0]), err)
return False return False
@ -385,16 +350,18 @@ class BaseVM(qubes.PropertyHolder):
subprocess.call(["sudo", "systemctl", "start", subprocess.call(["sudo", "systemctl", "start",
"qubes-reload-firewall@%s.timer" % self.name]) "qubes-reload-firewall@%s.timer" % self.name])
# XXX any better idea? some arguments? # SEE:1815 any better idea? some arguments?
self.fire_event('firewall-changed') self.fire_event('firewall-changed')
return True return True
def has_firewall(self): def has_firewall(self):
''' Return `True` if there are some vm specific firewall rules set '''
return os.path.exists(os.path.join(self.dir_path, self.firewall_conf)) return os.path.exists(os.path.join(self.dir_path, self.firewall_conf))
@staticmethod @staticmethod
def get_firewall_defaults(): def get_firewall_defaults():
''' Returns the default firewall rules '''
return { return {
'rules': list(), 'rules': list(),
'allow': True, 'allow': True,
@ -403,6 +370,7 @@ class BaseVM(qubes.PropertyHolder):
'allowYumProxy': False} 'allowYumProxy': False}
def get_firewall_conf(self): def get_firewall_conf(self):
''' Returns the firewall config dictionary '''
conf = self.get_firewall_defaults() conf = self.get_firewall_defaults()
try: try:
@ -459,7 +427,7 @@ class BaseVM(qubes.PropertyHolder):
conf["rules"].append(rule) conf["rules"].append(rule)
except EnvironmentError as err: # pylint: disable=broad-except except EnvironmentError as err: # pylint: disable=broad-except
# problem accessing file, like ENOTFOUND, EPERM or sth # problem accessing file, like ENOTFOUND, EPERM or sth
# return default config # return default config
return conf return conf
@ -506,7 +474,6 @@ class VMProperty(qubes.property):
self.vmclass = vmclass self.vmclass = vmclass
self.allow_none = allow_none self.allow_none = allow_none
def __set__(self, instance, value): def __set__(self, instance, value):
if value is self.__class__.DEFAULT: if value is self.__class__.DEFAULT:
self.__delete__(instance) self.__delete__(instance)

View File

@ -1,6 +1,5 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
# #
# The Qubes OS Project, https://www.qubes-os.org/ # The Qubes OS Project, https://www.qubes-os.org/
# #
@ -24,6 +23,8 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# #
''' This module contains the AdminVM implementation '''
import qubes import qubes
import qubes.exc import qubes.exc
import qubes.vm.qubesvm import qubes.vm.qubesvm
@ -41,7 +42,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
default=None, default=None,
doc='There are other ways to set kernel for Dom0.') doc='There are other ways to set kernel for Dom0.')
@property @property
def xid(self): def xid(self):
'''Always ``0``. '''Always ``0``.
@ -51,7 +51,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' '''
return 0 return 0
@property @property
def libvirt_domain(self): def libvirt_domain(self):
'''Always :py:obj:`None`. '''Always :py:obj:`None`.
@ -61,13 +60,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' '''
return None return None
# XXX probably unneeded, will return None as we don't have netvm
# @property
# def ip(self):
# return "10.137.0.2"
def is_running(self): def is_running(self):
'''Always :py:obj:`True`. '''Always :py:obj:`True`.
@ -76,7 +68,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' '''
return True return True
def get_power_state(self): def get_power_state(self):
'''Always ``'Running'``. '''Always ``'Running'``.
@ -85,7 +76,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' '''
return 'Running' return 'Running'
def get_mem(self): def get_mem(self):
'''Get current memory usage of Dom0. '''Get current memory usage of Dom0.
@ -101,7 +91,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
return int(line.split(':')[1].strip().split()[0]) return int(line.split(':')[1].strip().split()[0])
raise NotImplementedError() raise NotImplementedError()
def get_mem_static_max(self): def get_mem_static_max(self):
'''Get maximum memory available to Dom0. '''Get maximum memory available to Dom0.
@ -118,7 +107,6 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' '''
return True return True
def start(self, **kwargs): def start(self, **kwargs):
'''Always raises an exception. '''Always raises an exception.
@ -127,15 +115,13 @@ class AdminVM(qubes.vm.qubesvm.QubesVM):
''' # pylint: disable=unused-argument ''' # pylint: disable=unused-argument
raise qubes.exc.QubesVMError(self, 'Cannot start Dom0 fake domain!') raise qubes.exc.QubesVMError(self, 'Cannot start Dom0 fake domain!')
def suspend(self): def suspend(self):
'''Does nothing. '''Does nothing.
.. seealso: .. seealso:
:py:meth:`qubes.vm.qubesvm.QubesVM.suspend` :py:meth:`qubes.vm.qubesvm.QubesVM.suspend`
''' '''
# XXX shouldn't we spew an exception? raise qubes.exc.QubesVMError(self, 'Cannot suspend Dom0 fake domain!')
return
# def __init__(self, **kwargs): # def __init__(self, **kwargs):

View File

@ -1,5 +1,28 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014-2016 Wojtek Porczyk <woju@invisiblethingslab.com>
# Copyright (C) 2016 Marek Marczykowski <marmarek@invisiblethingslab.com>)
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
#
# 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.
#
''' This module contains the AppVM implementation '''
import qubes.events import qubes.events
import qubes.vm.qubesvm import qubes.vm.qubesvm
@ -45,7 +68,6 @@ class AppVM(qubes.vm.qubesvm.QubesVM):
@qubes.events.handler('domain-load') @qubes.events.handler('domain-load')
def on_domain_loaded(self, event): def on_domain_loaded(self, event):
# pylint: disable=unused-argument ''' When domain is loaded assert that this vm has a template.
# Some additional checks for template based VM ''' # pylint: disable=unused-argument
assert self.template assert self.template
# self.template.appvms.add(self) # XXX

View File

@ -1,5 +1,27 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014-2016 Wojtek Porczyk <woju@invisiblethingslab.com>
# Copyright (C) 2016 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.
#
''' A disposable vm implementation '''
import qubes.vm.qubesvm import qubes.vm.qubesvm
import qubes.vm.appvm import qubes.vm.appvm
@ -49,11 +71,9 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
@qubes.events.handler('domain-load') @qubes.events.handler('domain-load')
def on_domain_loaded(self, event): def on_domain_loaded(self, event):
# pylint: disable=unused-argument ''' When domain is loaded assert that this vm has a template.
# Some additional checks for template based VM ''' # pylint: disable=unused-argument
assert self.template assert self.template
# self.template.appvms.add(self) # XXX
@classmethod @classmethod
def from_appvm(cls, appvm, **kwargs): def from_appvm(cls, appvm, **kwargs):
@ -85,7 +105,6 @@ class DispVM(qubes.vm.qubesvm.QubesVM):
app.save() app.save()
return dispvm return dispvm
def cleanup(self): def cleanup(self):
'''Clean up after the DispVM '''Clean up after the DispVM

View File

@ -1,6 +1,5 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
# #
# The Qubes OS Project, https://www.qubes-os.org/ # The Qubes OS Project, https://www.qubes-os.org/
# #
@ -24,15 +23,18 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# #
''' This module contains the NetVMMixin '''
import re import re
import libvirt import libvirt # pylint: disable=import-error
import qubes import qubes
import qubes.events import qubes.events
import qubes.exc import qubes.exc
def _setter_mac(self, prop, value): def _setter_mac(self, prop, value):
''' Helper for setting the MAC address '''
# pylint: disable=unused-argument # pylint: disable=unused-argument
if not isinstance(value, basestring): if not isinstance(value, basestring):
raise ValueError('MAC address must be a string') raise ValueError('MAC address must be a string')
@ -41,14 +43,16 @@ def _setter_mac(self, prop, value):
raise ValueError('Invalid MAC address value') raise ValueError('Invalid MAC address value')
return value return value
class NetVMMixin(qubes.events.Emitter): class NetVMMixin(qubes.events.Emitter):
''' Mixin containing network functionality '''
mac = qubes.property('mac', type=str, mac = qubes.property('mac', type=str,
default='00:16:3E:5E:6C:00', default='00:16:3E:5E:6C:00',
setter=_setter_mac, setter=_setter_mac,
ls_width=17, ls_width=17,
doc='MAC address of the NIC emulated inside VM') doc='MAC address of the NIC emulated inside VM')
# XXX swallowed uses_default_netvm # CORE2: swallowed uses_default_netvm
netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True, netvm = qubes.VMProperty('netvm', load_stage=4, allow_none=True,
default=(lambda self: self.app.default_fw_netvm if self.provides_network default=(lambda self: self.app.default_fw_netvm if self.provides_network
else self.app.default_netvm), else self.app.default_netvm),
@ -62,7 +66,6 @@ class NetVMMixin(qubes.events.Emitter):
doc='''If this domain can act as network provider (formerly known as doc='''If this domain can act as network provider (formerly known as
NetVM or ProxyVM)''') NetVM or ProxyVM)''')
# #
# used in networked appvms or proxyvms (netvm is not None) # used in networked appvms or proxyvms (netvm is not None)
# #
@ -74,11 +77,10 @@ class NetVMMixin(qubes.events.Emitter):
if not self.is_networked(): if not self.is_networked():
return None return None
if self.netvm is not None: if self.netvm is not None:
return self.netvm.get_ip_for_vm(self) return self.netvm.get_ip_for_vm(self) # pylint: disable=no-member
else: else:
return self.get_ip_for_vm(self) return self.get_ip_for_vm(self)
# #
# used in netvms (provides_network=True) # used in netvms (provides_network=True)
# those properties and methods are most likely accessed as vm.netvm.<prop> # those properties and methods are most likely accessed as vm.netvm.<prop>
@ -88,7 +90,7 @@ class NetVMMixin(qubes.events.Emitter):
def get_ip_for_vm(vm): def get_ip_for_vm(vm):
'''Get IP address for (appvm) domain connected to this (netvm) domain. '''Get IP address for (appvm) domain connected to this (netvm) domain.
''' '''
import qubes.vm.dispvm # pylint: disable=redefined-outer-name import qubes.vm.dispvm # pylint: disable=redefined-outer-name
if isinstance(vm, qubes.vm.dispvm.DispVM): if isinstance(vm, qubes.vm.dispvm.DispVM):
return '10.138.{}.{}'.format((vm.dispid >> 8) & 7, vm.dispid & 7) return '10.138.{}.{}'.format((vm.dispid >> 8) & 7, vm.dispid & 7)
@ -108,22 +110,11 @@ class NetVMMixin(qubes.events.Emitter):
'''Netmask for gateway address.''' '''Netmask for gateway address.'''
return '255.255.255.255' if self.is_networked() else None 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
# XXX ugly hack ahead
# stubdom_xid is one more than self.xid
return 'vif{0}.+'.format(self.xid + int(self.hvm))
@property @property
def connected_vms(self): def connected_vms(self):
''' Return a generator containing all domains connected to the current
NetVM.
'''
for vm in self.app.domains: for vm in self.app.domains:
if vm.netvm is self: if vm.netvm is self:
yield vm yield vm
@ -144,27 +135,25 @@ class NetVMMixin(qubes.events.Emitter):
else: else:
return None return None
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(NetVMMixin, self).__init__(*args, **kwargs) super(NetVMMixin, self).__init__(*args, **kwargs)
@qubes.events.handler('domain-start') @qubes.events.handler('domain-start')
def on_domain_started(self, event, **kwargs): def on_domain_started(self, event, **kwargs):
'''Connect this domain to its downstream domains. Also reload firewall '''Connect this domain to its downstream domains. Also reload firewall
in its netvm. in its netvm.
This is needed when starting netvm *after* its connected domains. This is needed when starting netvm *after* its connected domains.
''' # pylint: disable=unused-argument ''' # pylint: disable=unused-argument
if self.netvm: if self.netvm:
self.netvm.reload_firewall_for_vm(self) self.netvm.reload_firewall_for_vm(self) # pylint: disable=no-member
for vm in self.connected_vms: for vm in self.connected_vms:
if not vm.is_running(): if not vm.is_running():
continue continue
vm.log.info('Attaching network') vm.log.info('Attaching network')
# 1426 # SEE: 1426
vm.cleanup_vifs() vm.cleanup_vifs()
try: try:
@ -179,10 +168,12 @@ class NetVMMixin(qubes.events.Emitter):
except qubes.exc.QubesException: except qubes.exc.QubesException:
vm.log.warning('Cannot attach network', exc_info=1) vm.log.warning('Cannot attach network', exc_info=1)
@qubes.events.handler('domain-pre-shutdown') @qubes.events.handler('domain-pre-shutdown')
def shutdown_net(self, event, force=False): def shutdown_net(self, event, force=False):
# pylint: disable=unused-argument ''' Checks before NetVM shutdown if any connected domains are running.
If `force` is `True` tries to detach network interfaces of connected
vms
''' # pylint: disable=unused-argument
connected_vms = [vm for vm in self.connected_vms if vm.is_running()] connected_vms = [vm for vm in self.connected_vms if vm.is_running()]
if connected_vms and not force: if connected_vms and not force:
@ -190,6 +181,7 @@ class NetVMMixin(qubes.events.Emitter):
'There are other VMs connected to this VM: {}'.format( 'There are other VMs connected to this VM: {}'.format(
', '.join(vm.name for vm in connected_vms))) ', '.join(vm.name for vm in connected_vms)))
# SEE: 1426
# detach network interfaces of connected VMs before shutting down, # detach network interfaces of connected VMs before shutting down,
# otherwise libvirt will not notice it and will try to detach them # otherwise libvirt will not notice it and will try to detach them
# again (which would fail, obviously). # again (which would fail, obviously).
@ -202,7 +194,6 @@ class NetVMMixin(qubes.events.Emitter):
# ignore errors # ignore errors
pass pass
def attach_network(self): def attach_network(self):
'''Attach network in this machine to it's netvm.''' '''Attach network in this machine to it's netvm.'''
@ -210,7 +201,8 @@ class NetVMMixin(qubes.events.Emitter):
raise qubes.exc.QubesVMNotRunningError(self) raise qubes.exc.QubesVMNotRunningError(self)
assert self.netvm is not None assert self.netvm is not None
if not self.netvm.is_running(): if not self.netvm.is_running(): # pylint: disable=no-member
# pylint: disable=no-member
self.log.info('Starting NetVM ({0})'.format(self.netvm.name)) self.log.info('Starting NetVM ({0})'.format(self.netvm.name))
self.netvm.start() self.netvm.start()
@ -218,7 +210,6 @@ class NetVMMixin(qubes.events.Emitter):
self.app.env.get_template('libvirt/devices/net.xml').render( self.app.env.get_template('libvirt/devices/net.xml').render(
vm=self)) vm=self))
def detach_network(self): def detach_network(self):
'''Detach machine from it's netvm''' '''Detach machine from it's netvm'''
@ -230,7 +221,6 @@ class NetVMMixin(qubes.events.Emitter):
self.app.env.get_template('libvirt/devices/net.xml').render( self.app.env.get_template('libvirt/devices/net.xml').render(
vm=self)) vm=self))
def is_networked(self): def is_networked(self):
'''Check whether this VM can reach network (firewall notwithstanding). '''Check whether this VM can reach network (firewall notwithstanding).
@ -244,7 +234,6 @@ class NetVMMixin(qubes.events.Emitter):
return self.netvm is not None return self.netvm is not None
def cleanup_vifs(self): def cleanup_vifs(self):
'''Remove stale network device backends. '''Remove stale network device backends.
@ -252,10 +241,6 @@ class NetVMMixin(qubes.events.Emitter):
it manually. This method is one big hack for #1426. 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 dev_basepath = '/local/domain/%d/device/vif' % self.xid
for dev in self.app.vmm.xs.ls('', dev_basepath): for dev in self.app.vmm.xs.ls('', dev_basepath):
# check if backend domain is alive # check if backend domain is alive
@ -270,11 +255,13 @@ class NetVMMixin(qubes.events.Emitter):
self.app.vmm.xs.rm('', '{}/{}'.format(dev_basepath, dev)) self.app.vmm.xs.rm('', '{}/{}'.format(dev_basepath, dev))
def reload_firewall_for_vm(self, vm): def reload_firewall_for_vm(self, vm):
# TODO QubesOS/qubes-issues#1815 ''' Reload the firewall rules for the vm '''
# SEE:1815
pass pass
@qubes.events.handler('property-del:netvm') @qubes.events.handler('property-del:netvm')
def on_property_del_netvm(self, event, prop, old_netvm=None): def on_property_del_netvm(self, event, prop, old_netvm=None):
''' Sets the the NetVM to default NetVM '''
# pylint: disable=unused-argument # pylint: disable=unused-argument
# we are changing to default netvm # we are changing to default netvm
new_netvm = self.netvm new_netvm = self.netvm
@ -282,9 +269,9 @@ class NetVMMixin(qubes.events.Emitter):
return return
self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm) self.fire_event('property-set:netvm', 'netvm', new_netvm, old_netvm)
@qubes.events.handler('property-pre-set:netvm') @qubes.events.handler('property-pre-set:netvm')
def on_property_pre_set_netvm(self, event, name, new_netvm, old_netvm=None): def on_property_pre_set_netvm(self, event, name, new_netvm, old_netvm=None):
''' Run sanity checks before setting a new NetVM '''
# pylint: disable=unused-argument # pylint: disable=unused-argument
if new_netvm is None: if new_netvm is None:
return return
@ -297,25 +284,24 @@ class NetVMMixin(qubes.events.Emitter):
or new_netvm in self.app.domains.get_vms_connected_to(self): or new_netvm in self.app.domains.get_vms_connected_to(self):
raise qubes.exc.QubesValueError('Loops in network are unsupported') raise qubes.exc.QubesValueError('Loops in network are unsupported')
# TODO offline_mode if not self.app.vmm.offline_mod and self.is_running() \
if self.is_running() and not new_netvm.is_running(): and not new_netvm.is_running():
raise qubes.exc.QubesVMNotStartedError(new_netvm, raise qubes.exc.QubesVMNotStartedError(new_netvm,
'Cannot dynamically attach to stopped NetVM: {!r}'.format( 'Cannot dynamically attach to stopped NetVM: {!r}'.format(
new_netvm)) new_netvm))
@qubes.events.handler('property-set:netvm') @qubes.events.handler('property-set:netvm')
def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None): def on_property_set_netvm(self, event, name, new_netvm, old_netvm=None):
''' Replaces the current NetVM with a new one and fires
net-domain-connect event
'''
# pylint: disable=unused-argument # pylint: disable=unused-argument
if self.netvm is not None: if self.netvm is not None:
if self.is_running(): if self.is_running():
self.detach_network() 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 new_netvm is None:
return return
@ -324,19 +310,23 @@ class NetVMMixin(qubes.events.Emitter):
self.create_qdb_entries() self.create_qdb_entries()
self.attach_network() self.attach_network()
# TODO documentation new_netvm.fire_event('net-domain-connect', self) # SEE: 1811
new_netvm.fire_event('net-domain-connect', self)
# FIXME handle in the above event? @qubes.events.handler('net-domain-connect')
new_netvm.reload_firewall_for_vm(self) def on_net_domain_connect(self, event, vm):
''' Reloads the firewall config for vm '''
# pylint: disable=unused-argument
self.reload_firewall_for_vm(vm)
@qubes.events.handler('domain-qdb-create') @qubes.events.handler('domain-qdb-create')
def on_domain_qdb_create(self, event): def on_domain_qdb_create(self, event):
# TODO: fill firewall QubesDB entries (QubesOS/qubes-issues#1815) ''' Fills the QubesDB with firewall entries. Not implemented '''
# SEE: 1815 fill firewall QubesDB entries
pass pass
# FIXME use event after creating Xen domain object, but before "resume" @qubes.events.handler('firewall-changed', 'domain-spawn')
@qubes.events.handler('firewall-changed')
def on_firewall_changed(self, event): def on_firewall_changed(self, event):
''' Reloads the firewall if vm is running and has a NetVM assigned '''
# pylint: disable=unused-argument # pylint: disable=unused-argument
if self.is_running() and self.netvm: if self.is_running() and self.netvm:
self.netvm.reload_firewall_for_vm(self) self.netvm.reload_firewall_for_vm(self) # pylint: disable=no-member

View File

@ -1,5 +1,28 @@
#!/usr/bin/python2 -O #!/usr/bin/python2 -O
# vim: fileencoding=utf-8 # vim: fileencoding=utf-8
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2014-2016 Wojtek Porczyk <woju@invisiblethingslab.com>
# Copyright (C) 2016 Marek Marczykowski <marmarek@invisiblethingslab.com>)
# Copyright (C) 2016 Bahtiar `kalkin-` Gadimov <bahtiar@gadimov.de>
#
# 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.
#
''' This module contains the TemplateVM implementation '''
import warnings import warnings
@ -24,6 +47,9 @@ class TemplateVM(QubesVM):
@property @property
def appvms(self): def appvms(self):
''' Returns a generator containing all domains based on the current
TemplateVM.
'''
for vm in self.app.domains: for vm in self.app.domains:
if hasattr(vm, 'template') and vm.template is self: if hasattr(vm, 'template') and vm.template is self:
yield vm yield vm