Skip to content
Snippets Groups Projects
node.py 4.50 KiB
# This file is part of CloudControl.
#
# CloudControl is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# CloudControl 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with CloudControl.  If not, see <http://www.gnu.org/licenses/>.


import os
import logging
import logging.config
from functools import wraps

from cloudcontrol.common.client.loop import RPCStartHandler, MainLoop
from cloudcontrol.common.client.tags import Tag

from cloudcontrol.node import __version__
from cloudcontrol.node.config import NodeConfigParser, configure_logging
from cloudcontrol.node.jobs import JobManager
from cloudcontrol.node.exc import ForbiddenHandler


logger = logging.getLogger(__name__)


def _rights_check(handler_name):
    """Method instance decorator for checking permissions before executing an
    RPC handler.

    """
    def decorator(func):
        @wraps(func)
        def decorated(*args, **kwargs):
            if handler_name in func.im_self.main.config.forbidden_handlers:
                logger.error('Remote tried to call forbidden handler "%s"',
                             handler_name)
                raise ForbiddenHandler('Forbidden handler "%s"' % handler_name)

            return func(*args, **kwargs)

        return decorated

    return decorator


class NodeRPCStartHandler(RPCStartHandler):
    def handle_authentication_response(self, response=None):
        # set handler according to which role was returned by the cc-server
        if response == self.loop.role and response is not None:
            # we don't need to reload the plugins
            # but we need to register the objects and tags
            self.loop.tag_db.rpc_register()
        elif response == u'host':
            # close previous plugins if needed
            if self.loop.role is not None:
                self.loop.close_plugins()
            logger.debug('Role host affected')
            from cloudcontrol.node.host import Handler as HostHandler
            self.loop.main_plugin = HostHandler(loop=self.loop)
            self.loop.role = 'host'
            # (re)-register the tags of the main loop
            self.loop.tag_db.rpc_register()
            self.loop.register_plugin(self.loop.main_plugin)
        elif response == u'hv':
            # close previous plugins if needed
            if self.loop.role is not None:
                self.loop.close_plugins()
            logger.debug('Role hypervisor affected')
            # set libvirt environement variables
            os.environ['LIBVIRT_DEBUG'] = '4'
            # os.environ['LIBVIRT_LOG_FILTERS'] = ''
            os.environ['LIBVIRT_LOG_OUTPUT'] = '4:stderr'
            # we don't import those modules at the top because some dependancies
            # may not be installed
            from cloudcontrol.node.hypervisor import Handler as HypervisorHandler
            self.loop.main_plugin = HypervisorHandler(
                hypervisor_name=self.loop.config.server_user,
                loop=self.loop,
            )
            self.loop.role = 'hv'
            # (re)-register the tags of the main loop
            self.loop.tag_db.rpc_register()
            self.loop.register_plugin(self.loop.main_plugin)
        else:
            logger.error('Failed authentication, role returned: %s', response)
            self._goto(self.handle_error)
            return

        self._goto(self.handle_done)


class NodeLoop(MainLoop):

    CONFIG_CLASS = NodeConfigParser
    CONNECT_CLASS = NodeRPCStartHandler
    DEFAULT_TAGS = (Tag(u'version', __version__),)

    def __init__(self, config_path):
        MainLoop.__init__(self, config_path)
        self.job_manager = JobManager(self)

    def reset_handler(self, name, handl):
        # we decorate each handler for permissions to be checked before each
        # invocation
        MainLoop.reset_handler(self, name, _rights_check(name)(handl))

    def configure_logging(self):
        configure_logging(self.config.logging_level, self.config.logging_output)

    def stop(self, watcher=None, revents=None):
        MainLoop.stop(self, watcher, revents)
        # stop running jobs
        self.job_manager.stop()