Coverage for src/lib/python/isc/config/ccsession : 92%
        
        
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
| 
 # Copyright (C) 2009 Internet Systems Consortium. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 # # Client-side functionality for configuration and commands # # It keeps a cc-channel session with the configuration manager daemon, # and handles configuration updates and direct commands 
 # modeled after ccsession.h/cc 'protocol' changes here need to be # made there as well 
 This module provides the ModuleCCSession and UIModuleCCSession classes, as well as a set of utility functions to create and parse messages related to commands and configuration 
 Modules should use the ModuleCCSession class to connect to the configuration manager, and receive updates and commands from other modules. 
 Configuration user interfaces should use the UIModuleCCSession to connect to b10-cmdctl, and receive and send configuration and commands through that to the configuration manager. """ 
 
 
 
 """Returns a tuple (rcode, value), where value depends on the command that was called. If rcode != 0, value is a string containing an error message""" else: else: 
 """Creates an answer packet for config&commands. rcode must be an integer. If rcode == 0, arg is an optional value that depends on what the command or option was. If rcode != 0, arg must be a string containing an error message""" else: 
 # 'fixed' commands """Fixed names for command and configuration messages""" 
 
 """Parses what may be a command message. If it looks like one, the function returns (command, value) where command is a string. If it is not, this function returns None, None""" 
 """Creates a module command message with the given command name (as specified in the module's specification, and an optional params object""" # TODO: validate_command with spec 
 
 json.dumps(config_data.get_module_spec().get_full_spec())) else: 
 """This class maintains a connection to the command channel, as well as configuration options for modules. The module provides a specification file that contains the module name, configuration options, and commands. It also gives the ModuleCCSession two callback functions, one to call when there is a direct command to the module, and one to update the configuration run-time. These callbacks are called when 'check_command' is called on the ModuleCCSession""" 
 cc_session=None, handle_logging_config=True, socket_file = None): """Initialize a ModuleCCSession. This does *NOT* send the specification and request the configuration yet. Use start() for that once the ModuleCCSession has been initialized. 
 specfile_name is the path to the specification file. 
 config_handler and command_handler are callback functions, see set_config_handler and set_command_handler for more information on their signatures. 
 cc_session can be used to pass in an existing CCSession, if it is None, one will be set up. This is mainly intended for testing purposes. 
 handle_logging_config: if True, the module session will automatically handle logging configuration for the module; it will read the system-wide Logging configuration and call the logger manager to apply it. It will also inform the logger manager when the logging configuration gets updated. The module does not need to do anything except intializing its loggers, and provide log messages. Defaults to true. 
 socket_file: If cc_session was none, this optional argument specifies which socket file to use to connect to msgq. It will be overridden by the environment variable MSGQ_SOCKET_FILE. If none, and no environment variable is set, it will use the system default. """ 
 
 
 else: 
 
 default_logconfig_handler) 
 # If the CC Session obejct has been closed, it returns # immediately. 
 """Send the specification for this module to the configuration manager, and request the current non-default configuration. The config_handler will be called with that configuration""" 
 """Sends a 'stopping' message to the configuration manager. This message is just an FYI, and no response is expected. Any errors when sending this message (for instance if the msgq session has previously been closed) are logged, but ignored.""" # create_command could raise an exception as well, but except for # out of memory related errors, these should all be programming # failures and are not caught self.get_module_spec().get_full_spec()) # If the session was previously closed, obvously trying to send # a message fails. (TODO: check if session is open so we can # error on real problems?) 
 """Returns the socket from the command channel session. This should *only* be used for select() loops to see if there is anything on the channel. If that loop is not completely time-critical, it is strongly recommended to only use check_command(), and not look at the socket at all.""" 
 """Close the session to the command channel""" 
 """Check whether there is a command or configuration update on the channel. This function does a read on the cc session, and returns nothing. It calls check_command_without_recvmsg() to parse the received message. 
 If nonblock is True, it just checks if there's a command and does nothing if there isn't. If nonblock is False, it waits until it arrives. It temporarily sets timeout to infinity, because commands may not come in arbitrary long time.""" finally: 
 """Parse the given message to see if there is a command or a configuration update. Calls the corresponding handler functions if present. Responds on the channel if the handler returns a message.""" # should we default to an answer? success-by-default? unhandled error? # If the target channel was not this module # it might be in the remote_module_configs # no checking for validity, that's up to the # module itself. self._remote_module_callbacks[module_name](new_config, self._remote_module_configs[module_name]) # For other modules, we're not supposed to answer 
 # ok, so apparently this update is for us. else: else: # ignore commands for 'remote' modules else: except Exception as exc: answer = create_answer(1, str(exc)) 
 """Set the config handler for this module. The handler is a function that takes the full configuration and handles it. It should return an answer created with create_answer()""" # should we run this right now since we've changed the handler? 
 """Set the command handler for this module. The handler is a function that takes a command as defined in the .spec file and return an answer created with create_answer()""" 
 config_update_callback=None): """The guts of add_remote_config and add_remote_config_by_name""" 
 
 # Get the current config for that module now 
 except isc.cc.SessionTimeout: raise ModuleCCSessionError("No answer from ConfigManager when " "asking about Remote module " + module_name) else: module_name + ": " + str(value)) else: "configuration data for " + module_name) 
 # all done, add it 
 config_update_callback=None): """ This does the same as add_remote_config, but you provide the module name instead of the name of the spec file. """ { "module_name": module_name }), "ConfigManager") except isc.cc.SessionTimeout: raise ModuleCCSessionError("No answer from ConfigManager when " + "asking about for spec of Remote " + "module " + module_name) module_name + " and " + module_spec.get_module_name()) config_update_callback) else: raise ModuleCCSessionError("Error code " + str(rcode) + "when asking for module spec of " + module_name) else: raise ModuleCCSessionError("No answer when asking for module " + "spec of " + module_name) # Just to be consistent with the add_remote_config 
 """Gives access to the configuration of a different module. These remote module options can at this moment only be accessed through get_remote_config_value(). This function also subscribes to the channel of the remote module name to receive the relevant updates. It is not possible to specify your own handler for this right now, but you can specify a callback that is called after the change happened. start() must have been called on this CCSession prior to the call to this method. Returns the name of the module.""" 
 """Removes the remote configuration access for this module""" 
 """Returns the current setting for the given identifier at the given module. If the module has not been added with add_remote_config, a ModuleCCSessionError is raised""" else: " not found") 
 """Sends the data specification to the configuration manager""" except isc.cc.SessionTimeout: # TODO: log an error? pass 
 """Asks the configuration manager for the current configuration, and call the config handler if set. Raises a ModuleCCSessionError if there is no answer from the configuration manager""" value, errors): else: "Wrong data in configuration: " + " ".join(errors)) else: else: raise ModuleCCSessionError("CC Session timeout waiting for configuration manager") 
 
 """This class is used in a configuration user interface. It contains specific functions for getting, displaying, and sending configuration settings through the b10-cmdctl module.""" """Initialize a UIModuleCCSession. The conn object that is passed must have send_GET and send_POST functions""" 
 """Clears the current list of specifications, and requests a new list from b10-cmdctl. As other actions may have caused modules to be stopped, or new modules to be added, this is expected to be run after each interaction (at this moment). It is usually also combined with request_current_config(). For that reason, we provide update_specs_and_config() which calls both.""" 
 """Requests the current configuration from the configuration manager through b10-cmdctl, and stores those as CURRENT. This does not modify any local changes, it just updates to the current state of the server itself.""" 
 """Convenience function to both clear and update the known list of module specifications, and update the current configuration on the server side. There are a few cases where the caller might only want to run one of these tasks, but often they are both needed.""" 
 
 
 raise isc.cc.data.DataNotFoundError( "No value given and no default for " + str(identifier)) 
 else: " already in " + str(identifier)) 
 raise isc.cc.data.DataTypeError("Name for named_set " + identifier + " must be a string") # fail on both None and empty string raise isc.cc.data.DataNotFoundError( "Need a name to add a new item to named_set " + str(identifier)) else: else: " already in " + identifier) 
 """Add a value to a configuration list. Raises a DataTypeError if the value does not conform to the list_item_spec field of the module config data specification. If value_str is not given, we add the default as specified by the .spec file. Raises a DataNotFoundError if the given identifier is not specified in the specification as a map or list. Raises a DataAlreadyPresentError if the specified element already exists.""" 
 # the specified element must be a list or a named_set # in lists, we might get the value with spaces, making it # the third argument. In that case we interpret both as # one big string meant as the value value_str += set_value_str item_value = isc.cc.data.parse_value_str(set_value_str) else: item_value) else: 
 # we are directly removing a list index else: else: 
 raise isc.cc.data.DataNotFoundError("Need a name to remove an item from named_set " + str(identifier)) raise isc.cc.data.DataTypeError("Name for named_set " + identifier + " must be a string") else: cur_map = {} else: 
 """Remove a value from a configuration list or named set. The value string must be a string representation of the full item. Raises a DataTypeError if the value at the identifier is not a list, or if the given value_str does not match the list_item_spec """ 
 
 else: 
 
 
 """Commit all local changes, send them through b10-cmdctl to the configuration manager""" [ self.get_local_changes() ]) # answer is either an empty dict (on success), or one # containing errors elif "error" in answer: raise ModuleCCSessionError("Error: " + str(answer["error"]) + "\n" + "Configuration not committed") else: raise ModuleCCSessionError("Unknown format of answer in commit(): " + str(answer))  |