示例#1
0
 def cmdhelp(self):
     """ Combined Help structure for all extensions. """
     try:
         return self.__help
     except AttributeError:
         self.__help = Help(self.extensions, logger=self.logger)
         return self.__help
示例#2
0
 def cmdhelp(self):
     u""" Combined Help structure for all extensions. """
     try:
         return self.__help
     except AttributeError:
         self.__help = Help(self.extensions, logger=self.logger)
         return self.__help
示例#3
0
    def load_extensions(self):
        """ Load BofhdExtensions (commands and help texts).

        This will load and initialize the BofhdExtensions specified by the
        configuration.

        """
        self.extensions = getattr(self, 'extensions', set())
        for cls in self.extensions:
            # Reload existing modules
            reload(sys.modules[cls.__module__])
        self.extensions = set()

        self.classmap.clear()
        self.commands.clear()

        for module_name, class_name in self.__config.extensions():
            mod = Utils.dyn_import(module_name)
            # TODO: Make dyn_import support class name
            try:
                cls = getattr(mod, class_name)
            except AttributeError:
                raise ImportError("Module '{}' has no class '{}'"
                                  .format(module_name, class_name))
            self.extensions.add(cls)

            # Map commands to BofhdExtensions
            # NOTE: Any duplicate command will be overloaded by later
            #       BofhdExtensions.
            for rpc in cls.list_commands('all_commands').keys():
                self.classmap[rpc] = cls
            for rpc in cls.list_commands('hidden_commands').keys():
                self.classmap[rpc] = cls

        # Check that all calls are implemented
        for rpc in sorted(self.classmap.keys()):
            if not hasattr(self.classmap[rpc], rpc):
                self.logger.warn("Warning, command %r is not implemented", rpc)
        self.__help = Help(self.extensions, logger=self.logger)

        self._log_help_text_mismatch()
        self._log_command_mismatch()
示例#4
0
    def load_extensions(self):
        """ Load BofhdExtensions (commands and help texts).

        This will load and initialize the BofhdExtensions specified by the
        configuration.

        """
        self.extensions = getattr(self, 'extensions', set())
        for cls in self.extensions:
            # Reload existing modules
            reload(sys.modules[cls.__module__])
        self.extensions = set()

        self.classmap.clear()
        self.commands.clear()

        for module_name, class_name in self.__config.extensions():
            mod = Utils.dyn_import(module_name)
            cls = getattr(mod, class_name)
            self.extensions.add(cls)

            # Map commands to BofhdExtensions
            # NOTE: Any duplicate command will be overloaded by later
            #       BofhdExtensions.
            for rpc in cls.list_commands('all_commands').keys():
                self.classmap[rpc] = cls
            for rpc in cls.list_commands('hidden_commands').keys():
                self.classmap[rpc] = cls

        # Check that all calls are implemented
        for rpc in sorted(self.classmap.keys()):
            if not hasattr(self.classmap[rpc], rpc):
                self.logger.warn("Warning, command '%s' is not implemented",
                                 rpc)
        self.__help = Help(self.extensions, logger=self.logger)

        self._log_help_text_mismatch()
        self._log_command_mismatch()
示例#5
0
class BofhdServerImplementation(object):
    """
    Common Server implementation.

    To get a functional server implementation, this class should be mixed in
    with a py:class:`SocketServer.BaseServer` implementation. E.g.:

    ::

        BofhdServer = type(
            'BofhdServer',
            (BofhdServerImplementation, SocketServer.TCPServer),
            {})

    """

    def __init__(
            self, bofhd_config=None, logRequests=False, logger=None, **kws):
        """ Set up a new bofhd server.

        :param BofhdConfig bofhd_config:
            A bofhd extension configuration.

        """
        self.__config = bofhd_config
        self.logger = logger.getChild('BofhdServerImplementation')
        super(BofhdServerImplementation, self).__init__(**kws)
        # TODO: logRequests is not really used anywhere?
        #       At least not here nor in SocketServer
        self.logRequests = logRequests
        self.load_extensions()
        # TODO: Not really used either
        self.server_start_time = time.time()

    @property
    @memoize
    def classmap(self):
        u""" Map of command_names and implementation classes.

        Each key is an implemented and callable command, and the value is its
        implementation class.
        """
        return Cache.Cache()

    @property
    def cmdhelp(self):
        """ Combined Help structure for all extensions. """
        try:
            return self.__help
        except AttributeError:
            self.__help = Help(self.extensions, logger=self.logger)
            return self.__help

    @property
    @memoize
    def commands(self):
        """ Cache of commands that a user has access to.

        It should contain info on every accessible command for every
        authenticated user:
            commands[entity_id][command_name] = Command.get_struct()
        """
        return Cache.Cache(
            mixins=[Cache.cache_mru, Cache.cache_slots, Cache.cache_timeout],
            size=500,
            timeout=60 * 60)

    @property
    @memoize
    def sessions(self):
        """ A cache that maps session id to entity id. """
        # Needed? Only used to throw ServerRestartedError...
        return Cache.Cache()

    @property
    @memoize
    def stats_config(self):
        """ Config for storing metrics using statsd. """
        try:
            return statsd_config.load_config()
        except Exception as e:
            self.logger.error("could not load statsd config (%r)", e)
            # default config
            return statsd_config.StatsConfig()

    def _log_help_text_mismatch(self):
        u""" Verify consistency of `self.cmdhelp`.

        Reports mismatch between loaded extensions and available help texts
        using the logger.
        """
        # Check that the help text is okay
        # Reformat the command definitions to be suitable for the help.
        cmds_for_help = dict()
        for cls in self.extensions:
            cmds_for_help.update(
                dict((cmdname, command.get_struct(self.cmdhelp))
                     for cmdname, command
                     in cls.list_commands('all_commands').iteritems()
                     if command and cmdname and self.classmap[cmdname] == cls))

        self.__help.check_consistency(cmds_for_help)

    def _log_command_mismatch(self):
        u""" Verify consistency of `self.classmap`.

        Reports mismatch between loaded extensions and available commands using
        the logger.
        """
        def fmt_class(cls):
            return '{0.__module__}/{0.__name__}'.format(cls)
        for cls in self.extensions:
            commands = cls.list_commands('all_commands')
            for key, cmd in commands.iteritems():
                if not key:
                    self.logger.warn('Skipping: Unnamed command %r', cmd)
                    continue
                if key not in self.classmap:
                    self.logger.warn('Skipping: No command %r in class map',
                                     key)
                    continue
                if cls is not self.classmap[key]:
                    self.logger.info(
                        'Skipping: Duplicate command %r'
                        ' (skipping=%s, using=%s)',
                        key,
                        fmt_class(cls),
                        fmt_class(self.classmap[key]))
                    continue

    def load_extensions(self):
        """ Load BofhdExtensions (commands and help texts).

        This will load and initialize the BofhdExtensions specified by the
        configuration.

        """
        self.extensions = getattr(self, 'extensions', set())
        for cls in self.extensions:
            # Reload existing modules
            reload(sys.modules[cls.__module__])
        self.extensions = set()

        self.classmap.clear()
        self.commands.clear()

        for module_name, class_name in self.__config.extensions():
            mod = Utils.dyn_import(module_name)
            # TODO: Make dyn_import support class name
            try:
                cls = getattr(mod, class_name)
            except AttributeError:
                raise ImportError("Module '{}' has no class '{}'"
                                  .format(module_name, class_name))
            self.extensions.add(cls)

            # Map commands to BofhdExtensions
            # NOTE: Any duplicate command will be overloaded by later
            #       BofhdExtensions.
            for rpc in cls.list_commands('all_commands').keys():
                self.classmap[rpc] = cls
            for rpc in cls.list_commands('hidden_commands').keys():
                self.classmap[rpc] = cls

        # Check that all calls are implemented
        for rpc in sorted(self.classmap.keys()):
            if not hasattr(self.classmap[rpc], rpc):
                self.logger.warn("Warning, command %r is not implemented", rpc)
        self.__help = Help(self.extensions, logger=self.logger)

        self._log_help_text_mismatch()
        self._log_command_mismatch()

    def get_cmd_info(self, rpc_name):
        """Return BofhdExtension and Command object for this cmd
        """
        cls = self.classmap[rpc_name]
        return (cls, cls.list_commands('all_commands')[rpc_name])

    # Override SocketServer.TCPServer (or subclass).
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        super(BofhdServerImplementation, self).server_bind()
        if hasattr(self, 'server_address'):
            logger.info("Ready to accept connections on %r",
                        format_addr(self.server_address))
        else:
            logger.info("Ready to accept connections")

    def close_request(self, request):
        """ Close request socket.

        process_request either leads to:
          - finish_request, which calls shutdown_request
          - handle_error, which calls shutdown_request

        shutdown_request should call close_request, with the request socket as
        argument.

        :param SSLSocket|socketobject request: The request socket to close.

        """
        super(BofhdServerImplementation, self).close_request(request)
        logger.debug("closed connection %r", request)
示例#6
0
class BofhdServerImplementation(object):
    """ Common Server implementation. """

    def __init__(
            self, bofhd_config=None, logRequests=False, logger=None, **kws):
        """ Set up a new bofhd server.

        :param BofhdConfig bofhd_config:
            A bofhd extension configuration.

        """
        self.__logger = logger
        self.__config = bofhd_config
        super(BofhdServerImplementation, self).__init__(**kws)
        # TODO: logRequests is not really used anywhere?
        #       At least not here nor in SocketServer
        self.logRequests = logRequests
        self.load_extensions()
        # TODO: Not really used either
        self.server_start_time = time.time()

    @property
    def logger(self):
        u""" Server logger. """
        if not self.__logger:
            self.__logger = Factory.get_logger()
        return self.__logger

    @property
    @memoize
    def classmap(self):
        u""" Map of command_names and implementation classes.

        Each key is an implemented and callable command, and the value is its
        implementation class.
        """
        return Cache.Cache()

    @property
    def cmdhelp(self):
        u""" Combined Help structure for all extensions. """
        try:
            return self.__help
        except AttributeError:
            self.__help = Help(self.extensions, logger=self.logger)
            return self.__help

    @property
    @memoize
    def commands(self):
        u""" Cache of commands that a user has access to.

        It should contain info on every accessible command for every
        authenticated user:
            commands[entity_id][command_name] = Command.get_struct()
        """
        return Cache.Cache(
            mixins=[Cache.cache_mru, Cache.cache_slots, Cache.cache_timeout],
            size=500,
            timeout=60 * 60)

    @property
    @memoize
    def sessions(self):
        u""" A cache that maps session id to entity id. """
        # Needed? Only used to throw ServerRestartedError...
        return Cache.Cache()

    def _log_help_text_mismatch(self):
        u""" Verify consistency of `self.cmdhelp`.

        Reports mismatch between loaded extensions and available help texts
        using the logger.
        """
        # Check that the help text is okay
        # Reformat the command definitions to be suitable for the help.
        cmds_for_help = dict()
        for cls in self.extensions:
            cmds_for_help.update(
                dict((cmdname, command.get_struct(self.cmdhelp))
                     for cmdname, command
                     in cls.list_commands('all_commands').iteritems()
                     if command and cmdname and self.classmap[cmdname] == cls))

        self.__help.check_consistency(cmds_for_help)

    def _log_command_mismatch(self):
        u""" Verify consistency of `self.classmap`.

        Reports mismatch between loaded extensions and available commands using
        the logger.
        """
        def fmt_class(cls):
            return u'{!s}/{!s}'.format(cls.__module__, cls.__name__)
        for cls in self.extensions:
            commands = cls.list_commands('all_commands')
            for key, cmd in commands.iteritems():
                if not key:
                    self.logger.warn(u'Skipping: Unnamed command %r', cmd)
                    continue
                if key not in self.classmap:
                    self.logger.warn(u'Skipping: No command %r in class map',
                                     key)
                    continue
                if cls is not self.classmap[key]:
                    self.logger.info(
                        u'Skipping: Duplicate command %r'
                        u' (skipping=%s, using=%s)',
                        key,
                        fmt_class(cls),
                        fmt_class(self.classmap[key]))
                    continue

    def load_extensions(self):
        """ Load BofhdExtensions (commands and help texts).

        This will load and initialize the BofhdExtensions specified by the
        configuration.

        """
        self.extensions = getattr(self, 'extensions', set())
        for cls in self.extensions:
            # Reload existing modules
            reload(sys.modules[cls.__module__])
        self.extensions = set()

        self.classmap.clear()
        self.commands.clear()

        for module_name, class_name in self.__config.extensions():
            mod = Utils.dyn_import(module_name)
            cls = getattr(mod, class_name)
            self.extensions.add(cls)

            # Map commands to BofhdExtensions
            # NOTE: Any duplicate command will be overloaded by later
            #       BofhdExtensions.
            for rpc in cls.list_commands('all_commands').keys():
                self.classmap[rpc] = cls
            for rpc in cls.list_commands('hidden_commands').keys():
                self.classmap[rpc] = cls

        # Check that all calls are implemented
        for rpc in sorted(self.classmap.keys()):
            if not hasattr(self.classmap[rpc], rpc):
                self.logger.warn("Warning, command '%s' is not implemented",
                                 rpc)
        self.__help = Help(self.extensions, logger=self.logger)

        self._log_help_text_mismatch()
        self._log_command_mismatch()

    def get_cmd_info(self, rpc_name):
        """Return BofhdExtension and Command object for this cmd
        """
        cls = self.classmap[rpc_name]
        return (cls, cls.list_commands('all_commands')[rpc_name])

    # Override SocketServer.TCPServer (or subclass).
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        super(BofhdServerImplementation, self).server_bind()
        if hasattr(self, 'server_address'):
            self.logger.info("Ready to accept connections on %r",
                             format_addr(self.server_address))
        else:
            self.logger.info("Ready to accept connections")

    def get_request(self):
        """ Get request socket and client address.

        This is used to log new connections.

        :rtype: tuple
        :return:
            A tuple with the request socket, and client address. The client
            address is a tuple consisting of address and port.

        """
        sock, addr = super(BofhdServerImplementation, self).get_request()
        self.logger.debug2("new connection from %s", format_addr(addr))
        return sock, addr

    def close_request(self, request):
        """ Close request socket.

        process_request either leads to:
          - finish_request, which calls shutdown_request
          - handle_error, which calls shutdown_request

        shutdown_request should call close_request, with the request socket as
        argument.

        :param SSLSocket|socketobject request: The request socket to close.

        """
        super(BofhdServerImplementation, self).close_request(request)
        self.logger.debug2("closed connection %r", request)