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.io
|
||||
import lxml.etree
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import qubes.config
|
||||
import qubes.events
|
||||
@ -1170,8 +1170,8 @@ class Qubes(PropertyHolder):
|
||||
self.log = logging.getLogger('app')
|
||||
|
||||
# pylint: disable=no-member
|
||||
self._extensions = set(
|
||||
ext(self) for ext in qubes.ext.Extension.register.values())
|
||||
self.extensions = set(ext.load()(self)
|
||||
for ext in pkg_resources.iter_entry_points('qubes.ext'))
|
||||
|
||||
#: collection of all VMs managed by this Qubes instance
|
||||
self.domains = VMCollection(self)
|
||||
@ -1234,7 +1234,7 @@ class Qubes(PropertyHolder):
|
||||
# stage 2: load VMs
|
||||
for node in self.xml.xpath('./domains/domain'):
|
||||
# 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.load_properties(load_stage=2)
|
||||
vm.init_log()
|
||||
@ -1384,6 +1384,29 @@ class Qubes(PropertyHolder):
|
||||
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):
|
||||
'''Add new Virtual Machine to colletion
|
||||
|
||||
@ -1502,7 +1525,3 @@ class Qubes(PropertyHolder):
|
||||
# fire property-del:netvm as it is responsible for resetting
|
||||
# netvm to it's default value
|
||||
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.
|
||||
'''
|
||||
|
||||
import inspect
|
||||
|
||||
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):
|
||||
'''Base class for all extensions
|
||||
@ -52,8 +38,6 @@ class Extension(object):
|
||||
:param qubes.Qubes app: application object
|
||||
''' # pylint: disable=too-few-public-methods
|
||||
|
||||
__metaclass__ = ExtensionPlugin
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
@ -100,7 +84,3 @@ def handler(*events, **kwargs):
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
__all__ = ['Extension', 'ExtensionPlugin', 'handler'] \
|
||||
+ qubes.plugins.load(__file__)
|
||||
|
@ -33,9 +33,9 @@ import qubes.ext
|
||||
import dbus
|
||||
|
||||
|
||||
class QubesManagerExtension(qubes.ext.Extension):
|
||||
class QubesManager(qubes.ext.Extension):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(QubesManagerExtension, self).__init__(*args, **kwargs)
|
||||
super(QubesManager, self).__init__(*args, **kwargs)
|
||||
self._system_bus = dbus.SystemBus()
|
||||
|
||||
# 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.adminvm',
|
||||
'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
|
||||
'qubes.tests.int.basic',
|
||||
'qubes.tests.int.dom0_update',
|
||||
|
@ -1,14 +1 @@
|
||||
# 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)))
|
||||
|
||||
try:
|
||||
cls = qubes.vm.BaseVM.register[args.cls] # pylint: disable=no-member
|
||||
cls = args.app.get_vm_class(args.cls)
|
||||
except KeyError:
|
||||
parser.error('no such domain class: {!r}'.format(args.cls))
|
||||
|
||||
|
@ -44,7 +44,6 @@ import lxml.etree
|
||||
import qubes
|
||||
import qubes.log
|
||||
import qubes.events
|
||||
import qubes.plugins
|
||||
import qubes.tools.qvm_ls
|
||||
|
||||
|
||||
@ -127,7 +126,7 @@ class Features(dict):
|
||||
return default
|
||||
|
||||
|
||||
class BaseVMMeta(qubes.plugins.Plugin, qubes.events.EmitterMeta):
|
||||
class BaseVMMeta(qubes.events.EmitterMeta):
|
||||
'''Metaclass for :py:class:`.BaseVM`'''
|
||||
def __init__(cls, name, bases, dict_):
|
||||
super(BaseVMMeta, cls).__init__(name, bases, dict_)
|
||||
@ -649,7 +648,3 @@ class BaseVM(qubes.PropertyHolder):
|
||||
return None
|
||||
|
||||
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):
|
||||
warnings.warn('vm.is_template() is deprecated, use isinstance()',
|
||||
DeprecationWarning)
|
||||
import qubes.vm.templatevm
|
||||
return isinstance(self, qubes.vm.templatevm.TemplateVM)
|
||||
|
||||
def is_appvm(self):
|
||||
warnings.warn('vm.is_appvm() is deprecated, use isinstance()',
|
||||
DeprecationWarning)
|
||||
import qubes.vm.appvm
|
||||
return isinstance(self, qubes.vm.appvm.AppVM)
|
||||
|
||||
def is_proxyvm(self):
|
||||
@ -407,6 +409,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
|
||||
def is_disposablevm(self):
|
||||
warnings.warn('vm.is_disposable() is deprecated, use isinstance()',
|
||||
DeprecationWarning)
|
||||
import qubes.vm.dispvm
|
||||
return isinstance(self, qubes.vm.dispvm.DispVM)
|
||||
|
||||
def is_netvm(self):
|
||||
|
@ -201,13 +201,11 @@ fi
|
||||
|
||||
%dir %{python_sitelib}/qubes
|
||||
%{python_sitelib}/qubes/__init__.py*
|
||||
%{python_sitelib}/qubes/_pluginloader.py*
|
||||
%{python_sitelib}/qubes/config.py*
|
||||
%{python_sitelib}/qubes/dochelpers.py*
|
||||
%{python_sitelib}/qubes/events.py*
|
||||
%{python_sitelib}/qubes/exc.py*
|
||||
%{python_sitelib}/qubes/log.py*
|
||||
%{python_sitelib}/qubes/plugins.py*
|
||||
%{python_sitelib}/qubes/rngdoc.py*
|
||||
%{python_sitelib}/qubes/utils.py*
|
||||
|
||||
|
8
setup.py
8
setup.py
@ -29,5 +29,13 @@ if __name__ == '__main__':
|
||||
|
||||
entry_points={
|
||||
'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