NetVMMixin add docstrings & fix pylint errors

This commit is contained in:
Bahtiar `kalkin-` Gadimov 2016-06-16 16:39:48 +02:00
parent b77c36b224
commit 91ee455a37

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)
@ -110,6 +112,9 @@ class NetVMMixin(qubes.events.Emitter):
@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
@ -130,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:
@ -165,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:
@ -176,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).
@ -188,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.'''
@ -196,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()
@ -204,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'''
@ -216,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).
@ -230,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.
@ -238,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
@ -256,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
@ -268,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
@ -283,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
@ -310,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