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
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
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 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()
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)
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)