core-admin/qubes/vm/__init__.py

217 lines
5.4 KiB
Python
Raw Normal View History

2014-11-13 14:38:41 +01:00
#!/usr/bin/python2 -O
2014-11-13 18:10:27 +01:00
'''Qubes Virtual Machines
Main public classes
-------------------
.. autoclass:: BaseVM
:members:
:show-inheritance:
.. autoclass:: property
:members:
:show-inheritance:
Helper classes and functions
----------------------------
.. autoclass:: VMPlugin
:members:
:show-inheritance:
Particular VM classes
---------------------
Main types:
.. toctree::
:maxdepth: 1
qubesvm
appvm
templatevm
Special VM types:
.. toctree::
:maxdepth: 1
netvm
proxyvm
dispvm
adminvm
HVMs:
.. toctree::
:maxdepth: 1
hvm
templatehvm
'''
2014-11-18 17:35:05 +01:00
import ast
2014-11-13 14:38:41 +01:00
import collections
import functools
import sys
import dateutil.parser
import qubes.plugins
class property(object):
2014-11-13 18:10:27 +01:00
'''Qubes VM property.
This class holds one property that can be saved and loaded from qubes.xml
:param str name: name of the property
:param object default: default value
:param type type: if not :py:obj:`None`, this is used to initialise value
:param int order: order of evaluation (bigger order values are later)
:param str doc: docstring
'''
2014-11-13 14:38:41 +01:00
def __init__(self, name, default=None, type=None, order=0, doc=None):
self.__name__ = name
self._default = default
self._type = type
self.order = order
self.__doc__ = doc
self._attr_name = '_qubesprop_' + self.__name__
def __get__(self, instance, owner):
if instance is None:
return self
try:
return getattr(instance, self._attr_name)
except AttributeError:
if self._default is None:
raise AttributeError('property not set')
else:
return self._default
def __set__(self, instance, value):
setattr(instance, self._attr_name,
(self._type(value) if self._type is not None else value))
def __repr__(self):
return '<{} object at {:#x} name={!r} default={!r}>'.format(
self.__class__.__name__, id(self), self.__name__, self._default)
def __hash__(self):
return hash(self.__name__)
def __eq__(self, other):
return self.__name__ == other.__name__
class VMPlugin(qubes.plugins.Plugin):
2014-11-13 18:10:27 +01:00
'''Metaclass for :py:class:`.BaseVM`'''
2014-11-13 14:38:41 +01:00
def __init__(cls, name, bases, dict_):
super(VMPlugin, cls).__init__(name, bases, dict_)
cls.__hooks__ = collections.defaultdict(list)
class BaseVM(object):
2014-11-18 17:35:05 +01:00
'''Base class for all VMs
:param xml: xml node from which to deserialise
:type xml: :py:class:`lxml.etree._Element` or :py:obj:`None`
This class is responsible for serialising and deserialising machines and
provides basic framework. It contains no management logic. For that, see
:py:class:`qubes.vm.qubesvm.QubesVM`.
'''
2014-11-13 14:38:41 +01:00
__metaclass__ = VMPlugin
def get_props_list(self):
2014-11-13 18:10:27 +01:00
'''List all properties attached to this VM'''
2014-11-13 14:38:41 +01:00
props = set()
for class_ in self.__class__.__mro__:
props.update(prop for prop in class_.__dict__.values()
if isinstance(prop, property))
return sorted(props, key=lambda prop: (prop.order, prop.__name__))
2014-11-18 17:35:05 +01:00
def __init__(self, xml):
self._xml = xml
self.services = {}
self.devices = collections.defaultdict(list)
self.tags = {}
if self._xml is None:
return
# properties
all_names = set(prop.__name__ for prop in self.get_props_list())
for node in self._xml.xpath('.//property'):
name = node.get('name')
value = node.get('ref') or node.text
if not name in all_names:
raise AttributeError(
'No property {!r} found in {!r}'.format(
name, self.__class__))
setattr(self, name, value)
# tags
for node in self._xml.xpath('.//tag'):
self.tags[node.get('name')] = node.text
# services
for node in self._xml.xpath('.//service'):
self.services[node.text] = bool(ast.literal_eval(node.get('enabled', 'True')))
# devices (pci, usb, ...)
for parent in self._xml.xpath('.//devices'):
devclass = parent.get('class')
for node in parent.xpath('./device'):
self.devices[devclass].append(node.text)
# firewall
#TODO
2014-11-13 14:38:41 +01:00
def __repr__(self):
return '<{} object at {:#x} {}>'.format(
self.__class__.__name__, id(self),
' '.join('{}={}'.format(prop.__name__, getattr(self, prop.__name__))
for prop in self.get_props_list()))
@classmethod
def add_hook(cls, event, f):
2014-11-13 18:10:27 +01:00
'''Add hook to entire VM class and all subclasses
:param str event: event type
:param callable f: function to fire on event
Prototype of the function depends on the exact type of event. Classes
which inherit from this class will also inherit the hook.
'''
2014-11-13 14:38:41 +01:00
cls.__hooks__[event].append(f)
def fire_hooks(self, event, *args, **kwargs):
2014-11-18 17:35:05 +01:00
'''Fire hooks associated with an event
2014-11-13 18:10:27 +01:00
:param str event: event type
*args* and *kwargs* are passed to each function
'''
2014-11-13 14:38:41 +01:00
for cls in self.__class__.__mro__:
if not hasattr(cls, '__hooks__'): continue
for hook in cls.__hooks__[event]:
hook(self, *args, **kwargs)
def load(class_, D):
cls = BaseVM[class_]
return cls(D)
__all__ = qubes.plugins.load(__file__)