qubes: Convert QubesVM and Extension discovery to pkg_resources
QubesOS/qubes-issues#1238
This commit is contained in:
parent
93686eae06
commit
d09bd5ab6a
@ -54,7 +54,7 @@ import __builtin__
|
|||||||
import docutils.core
|
import docutils.core
|
||||||
import docutils.io
|
import docutils.io
|
||||||
import lxml.etree
|
import lxml.etree
|
||||||
|
import pkg_resources
|
||||||
|
|
||||||
import qubes.config
|
import qubes.config
|
||||||
import qubes.events
|
import qubes.events
|
||||||
@ -1170,8 +1170,8 @@ class Qubes(PropertyHolder):
|
|||||||
self.log = logging.getLogger('app')
|
self.log = logging.getLogger('app')
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
self._extensions = set(
|
self.extensions = set(ext.load()(self)
|
||||||
ext(self) for ext in qubes.ext.Extension.register.values())
|
for ext in pkg_resources.iter_entry_points('qubes.ext'))
|
||||||
|
|
||||||
#: collection of all VMs managed by this Qubes instance
|
#: collection of all VMs managed by this Qubes instance
|
||||||
self.domains = VMCollection(self)
|
self.domains = VMCollection(self)
|
||||||
@ -1234,7 +1234,7 @@ class Qubes(PropertyHolder):
|
|||||||
# stage 2: load VMs
|
# stage 2: load VMs
|
||||||
for node in self.xml.xpath('./domains/domain'):
|
for node in self.xml.xpath('./domains/domain'):
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
cls = qubes.vm.BaseVM.register[node.get('class')]
|
cls = self.get_vm_class(node.get('class'))
|
||||||
vm = cls(self, node)
|
vm = cls(self, node)
|
||||||
vm.load_properties(load_stage=2)
|
vm.load_properties(load_stage=2)
|
||||||
vm.init_log()
|
vm.init_log()
|
||||||
@ -1384,6 +1384,29 @@ class Qubes(PropertyHolder):
|
|||||||
return labels
|
return labels
|
||||||
|
|
||||||
|
|
||||||
|
def get_vm_class(self, clsname):
|
||||||
|
'''Find the class for a domain.
|
||||||
|
|
||||||
|
Classess are registered as setuptools' entry points in ``qubes.vm``
|
||||||
|
group. Any package may supply their own classess.
|
||||||
|
|
||||||
|
:param str clsname: name of the class
|
||||||
|
:return type: class
|
||||||
|
'''
|
||||||
|
epoints = tuple(pkg_resources.iter_entry_points('qubes.vm', clsname))
|
||||||
|
if not epoints:
|
||||||
|
raise qubes.exc.QubesException(
|
||||||
|
'no such VM class: {!r}'.format(clsname))
|
||||||
|
elif len(epoints) > 1:
|
||||||
|
raise qubes.exc.QubesException(
|
||||||
|
'more than 1 implementation of {!r} found: {}'.format(
|
||||||
|
clsname,
|
||||||
|
', '.join(
|
||||||
|
'{}.{}'.format(ep.module_name, '.'.join(ep.attrs))
|
||||||
|
for ep in epoints)))
|
||||||
|
return epoints[0].load()
|
||||||
|
|
||||||
|
|
||||||
def add_new_vm(self, cls, qid=None, **kwargs):
|
def add_new_vm(self, cls, qid=None, **kwargs):
|
||||||
'''Add new Virtual Machine to colletion
|
'''Add new Virtual Machine to colletion
|
||||||
|
|
||||||
@ -1502,7 +1525,3 @@ class Qubes(PropertyHolder):
|
|||||||
# fire property-del:netvm as it is responsible for resetting
|
# fire property-del:netvm as it is responsible for resetting
|
||||||
# netvm to it's default value
|
# netvm to it's default value
|
||||||
vm.fire_event('property-del:netvm', 'netvm', newvalue, oldvalue)
|
vm.fire_event('property-del:netvm', 'netvm', newvalue, oldvalue)
|
||||||
|
|
||||||
|
|
||||||
# load plugins
|
|
||||||
import qubes._pluginloader
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
#!/usr/bin/python2 -O
|
|
||||||
# vim: fileencoding=utf-8
|
|
||||||
# pylint: disable=wildcard-import,unused-wildcard-import
|
|
||||||
|
|
||||||
from qubes.vm import *
|
|
||||||
from qubes.ext import *
|
|
||||||
|
|
@ -29,22 +29,8 @@ some systems. They may be OS- or architecture-dependent or custom-developed for
|
|||||||
particular customer.
|
particular customer.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
import qubes.events
|
import qubes.events
|
||||||
import qubes.plugins
|
|
||||||
|
|
||||||
class ExtensionPlugin(qubes.plugins.Plugin):
|
|
||||||
'''Metaclass for :py:class:`Extension`'''
|
|
||||||
def __init__(cls, name, bases, dict_):
|
|
||||||
super(ExtensionPlugin, cls).__init__(name, bases, dict_)
|
|
||||||
cls._instance = None
|
|
||||||
|
|
||||||
def __call__(cls, *args, **kwargs):
|
|
||||||
if cls._instance is None:
|
|
||||||
cls._instance = super(ExtensionPlugin, cls).__call__(
|
|
||||||
*args, **kwargs)
|
|
||||||
return cls._instance
|
|
||||||
|
|
||||||
class Extension(object):
|
class Extension(object):
|
||||||
'''Base class for all extensions
|
'''Base class for all extensions
|
||||||
@ -52,8 +38,6 @@ class Extension(object):
|
|||||||
:param qubes.Qubes app: application object
|
:param qubes.Qubes app: application object
|
||||||
''' # pylint: disable=too-few-public-methods
|
''' # pylint: disable=too-few-public-methods
|
||||||
|
|
||||||
__metaclass__ = ExtensionPlugin
|
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
@ -100,7 +84,3 @@ def handler(*events, **kwargs):
|
|||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Extension', 'ExtensionPlugin', 'handler'] \
|
|
||||||
+ qubes.plugins.load(__file__)
|
|
||||||
|
@ -33,9 +33,9 @@ import qubes.ext
|
|||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
|
|
||||||
class QubesManagerExtension(qubes.ext.Extension):
|
class QubesManager(qubes.ext.Extension):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(QubesManagerExtension, self).__init__(*args, **kwargs)
|
super(QubesManager, self).__init__(*args, **kwargs)
|
||||||
self._system_bus = dbus.SystemBus()
|
self._system_bus = dbus.SystemBus()
|
||||||
|
|
||||||
# pylint: disable=no-self-use,unused-argument,too-few-public-methods
|
# pylint: disable=no-self-use,unused-argument,too-few-public-methods
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
#!/usr/bin/python2 -O
|
|
||||||
# vim: fileencoding=utf-8
|
|
||||||
|
|
||||||
#
|
|
||||||
# The Qubes OS Project, https://www.qubes-os.org/
|
|
||||||
#
|
|
||||||
# Copyright (C) 2014-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
||||||
# Copyright (C) 2014-2015 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
'''Plugins helpers for Qubes
|
|
||||||
|
|
||||||
Qubes uses two types of plugins: virtual machines and extensions.
|
|
||||||
'''
|
|
||||||
|
|
||||||
import imp
|
|
||||||
import os
|
|
||||||
|
|
||||||
class Plugin(type):
|
|
||||||
'''Base metaclass for plugins'''
|
|
||||||
def __init__(cls, name, bases, dict_):
|
|
||||||
super(Plugin, cls).__init__(name, bases, dict_)
|
|
||||||
# pylint: disable=unused-argument
|
|
||||||
if hasattr(cls, 'register'):
|
|
||||||
cls.register[cls.__name__] = cls
|
|
||||||
else:
|
|
||||||
# we've got root class
|
|
||||||
cls.register = {}
|
|
||||||
|
|
||||||
|
|
||||||
def load(modfile):
|
|
||||||
'''Load (import) all plugins from subpackage.
|
|
||||||
|
|
||||||
This function should be invoked from ``__init__.py`` in a package like that:
|
|
||||||
|
|
||||||
>>> __all__ = qubes.plugins.load(__file__) # doctest: +SKIP
|
|
||||||
'''
|
|
||||||
|
|
||||||
path = os.path.dirname(modfile)
|
|
||||||
listdir = os.listdir(path)
|
|
||||||
ret = set()
|
|
||||||
|
|
||||||
# pylint: disable=unused-variable
|
|
||||||
for suffix, mode, type_ in imp.get_suffixes():
|
|
||||||
for filename in listdir:
|
|
||||||
if filename.endswith(suffix):
|
|
||||||
ret.add(filename[:-len(suffix)])
|
|
||||||
|
|
||||||
if '__init__' in ret:
|
|
||||||
ret.remove('__init__')
|
|
||||||
|
|
||||||
return list(sorted(ret))
|
|
@ -786,8 +786,13 @@ def load_tests(loader, tests, pattern): # pylint: disable=unused-argument
|
|||||||
'qubes.tests.vm.qubesvm',
|
'qubes.tests.vm.qubesvm',
|
||||||
'qubes.tests.vm.adminvm',
|
'qubes.tests.vm.adminvm',
|
||||||
'qubes.tests.init2',
|
'qubes.tests.init2',
|
||||||
'qubes.tests.tools',
|
):
|
||||||
|
tests.addTests(loader.loadTestsFromName(modname))
|
||||||
|
|
||||||
|
tests.addTests(loader.discover(
|
||||||
|
os.path.join(os.path.dirname(__file__), 'tools')))
|
||||||
|
|
||||||
|
for modname in (
|
||||||
# integration tests
|
# integration tests
|
||||||
'qubes.tests.int.basic',
|
'qubes.tests.int.basic',
|
||||||
'qubes.tests.int.dom0_update',
|
'qubes.tests.int.dom0_update',
|
||||||
|
@ -1,14 +1 @@
|
|||||||
# pylint: skip-file
|
# pylint: skip-file
|
||||||
|
|
||||||
import importlib
|
|
||||||
|
|
||||||
import qubes.plugins
|
|
||||||
import qubes.tests
|
|
||||||
|
|
||||||
__all__ = qubes.plugins.load(__file__)
|
|
||||||
|
|
||||||
def load_tests(loader, tests, pattern):
|
|
||||||
for name in __all__:
|
|
||||||
mod = importlib.import_module('.' + name, __name__)
|
|
||||||
tests.addTests(loader.loadTestsFromModule(mod))
|
|
||||||
return tests
|
|
||||||
|
@ -93,7 +93,7 @@ def main(args=None):
|
|||||||
', '.join(repr(l.name) for l in args.app.labels)))
|
', '.join(repr(l.name) for l in args.app.labels)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cls = qubes.vm.BaseVM.register[args.cls] # pylint: disable=no-member
|
cls = args.app.get_vm_class(args.cls)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
parser.error('no such domain class: {!r}'.format(args.cls))
|
parser.error('no such domain class: {!r}'.format(args.cls))
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ import lxml.etree
|
|||||||
import qubes
|
import qubes
|
||||||
import qubes.log
|
import qubes.log
|
||||||
import qubes.events
|
import qubes.events
|
||||||
import qubes.plugins
|
|
||||||
import qubes.tools.qvm_ls
|
import qubes.tools.qvm_ls
|
||||||
|
|
||||||
|
|
||||||
@ -127,7 +126,7 @@ class Features(dict):
|
|||||||
return default
|
return default
|
||||||
|
|
||||||
|
|
||||||
class BaseVMMeta(qubes.plugins.Plugin, qubes.events.EmitterMeta):
|
class BaseVMMeta(qubes.events.EmitterMeta):
|
||||||
'''Metaclass for :py:class:`.BaseVM`'''
|
'''Metaclass for :py:class:`.BaseVM`'''
|
||||||
def __init__(cls, name, bases, dict_):
|
def __init__(cls, name, bases, dict_):
|
||||||
super(BaseVMMeta, cls).__init__(name, bases, dict_)
|
super(BaseVMMeta, cls).__init__(name, bases, dict_)
|
||||||
@ -649,7 +648,3 @@ class BaseVM(qubes.PropertyHolder):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['BaseVMMeta', 'DeviceCollection', 'DeviceManager', 'BaseVM'] \
|
|
||||||
+ qubes.plugins.load(__file__)
|
|
||||||
|
@ -392,11 +392,13 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
def is_template(self):
|
def is_template(self):
|
||||||
warnings.warn('vm.is_template() is deprecated, use isinstance()',
|
warnings.warn('vm.is_template() is deprecated, use isinstance()',
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
|
import qubes.vm.templatevm
|
||||||
return isinstance(self, qubes.vm.templatevm.TemplateVM)
|
return isinstance(self, qubes.vm.templatevm.TemplateVM)
|
||||||
|
|
||||||
def is_appvm(self):
|
def is_appvm(self):
|
||||||
warnings.warn('vm.is_appvm() is deprecated, use isinstance()',
|
warnings.warn('vm.is_appvm() is deprecated, use isinstance()',
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
|
import qubes.vm.appvm
|
||||||
return isinstance(self, qubes.vm.appvm.AppVM)
|
return isinstance(self, qubes.vm.appvm.AppVM)
|
||||||
|
|
||||||
def is_proxyvm(self):
|
def is_proxyvm(self):
|
||||||
@ -407,6 +409,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
|||||||
def is_disposablevm(self):
|
def is_disposablevm(self):
|
||||||
warnings.warn('vm.is_disposable() is deprecated, use isinstance()',
|
warnings.warn('vm.is_disposable() is deprecated, use isinstance()',
|
||||||
DeprecationWarning)
|
DeprecationWarning)
|
||||||
|
import qubes.vm.dispvm
|
||||||
return isinstance(self, qubes.vm.dispvm.DispVM)
|
return isinstance(self, qubes.vm.dispvm.DispVM)
|
||||||
|
|
||||||
def is_netvm(self):
|
def is_netvm(self):
|
||||||
|
@ -201,13 +201,11 @@ fi
|
|||||||
|
|
||||||
%dir %{python_sitelib}/qubes
|
%dir %{python_sitelib}/qubes
|
||||||
%{python_sitelib}/qubes/__init__.py*
|
%{python_sitelib}/qubes/__init__.py*
|
||||||
%{python_sitelib}/qubes/_pluginloader.py*
|
|
||||||
%{python_sitelib}/qubes/config.py*
|
%{python_sitelib}/qubes/config.py*
|
||||||
%{python_sitelib}/qubes/dochelpers.py*
|
%{python_sitelib}/qubes/dochelpers.py*
|
||||||
%{python_sitelib}/qubes/events.py*
|
%{python_sitelib}/qubes/events.py*
|
||||||
%{python_sitelib}/qubes/exc.py*
|
%{python_sitelib}/qubes/exc.py*
|
||||||
%{python_sitelib}/qubes/log.py*
|
%{python_sitelib}/qubes/log.py*
|
||||||
%{python_sitelib}/qubes/plugins.py*
|
|
||||||
%{python_sitelib}/qubes/rngdoc.py*
|
%{python_sitelib}/qubes/rngdoc.py*
|
||||||
%{python_sitelib}/qubes/utils.py*
|
%{python_sitelib}/qubes/utils.py*
|
||||||
|
|
||||||
|
8
setup.py
8
setup.py
@ -29,5 +29,13 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': list(get_console_scripts()),
|
'console_scripts': list(get_console_scripts()),
|
||||||
|
'qubes.vm': [
|
||||||
|
'AppVM = qubes.vm.appvm:AppVM',
|
||||||
|
'TemplateVM = qubes.vm.templatevm:TemplateVM',
|
||||||
|
'AdminVM = qubes.vm.adminvm:AdminVM',
|
||||||
|
],
|
||||||
|
'qubes.ext': [
|
||||||
|
'qubes.ext.qubesmanager = qubes.ext.qubesmanager:QubesManager',
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user