openrvdas.server.server_api

API for interacting with data store. Implementations should subclass.

  1#!/usr/bin/env python3
  2"""
  3API for interacting with data store. Implementations should subclass.
  4"""
  5import logging
  6import sys
  7
  8
  9################################################################################
 10class ServerAPI:
 11    """Abstract base class defining an API through which a LoggerServer
 12    can interact with a data store.
 13
 14    Parameters below have the following semantics:
 15    ```
 16    configuration - dict definition of a OpenRVDAS configuration
 17    mode          - dict name of mode, and logger_config_names associated with that mode
 18    default_mode  - name of mode to use at startup or when returning to a default state
 19
 20    logger_config_name - string name of a logger configuration, unique within configration
 21    logger_config - dict definition of a logger configuration
 22
 23    logger_id     - string name of logger, unique within cruise
 24    logger        - dict definition of logger, including list of names of
 25                    valid configs and optional host restriction
 26
 27    logger_configs - dict of {logger_config_name:logger_config,...}
 28    ```
 29    For the purposes of documentation below, assume a sample
 30    cruise_config as follows:
 31    ```
 32    {
 33      "loggers": {
 34        "knud": {
 35          "host": "knud.pi",
 36          "configs": ["off", "knud->net", "knud->file/net/db"]
 37        },
 38        "gyr1": {
 39          "configs": ["off", "gyr1->net", "gyr1->file/net/db"]
 40        },
 41      "modes": {
 42        "off": {"knud": "off", "gyr1": "off"},
 43        "port": {"knud": "off", "gyr1": "gyr1->net"},
 44        "underway": { "knud": "knud->file/net/db",
 45                      "gyr1": "gyr1->file/net/db"
 46                    }
 47      },
 48      "default_mode": "off",
 49      "configs": {
 50        "off": {},
 51        "knud->net": { config_spec },
 52        "knud->file/net/db": { config_spec },
 53        "gyr1->net": { config_spec },
 54        "gyr1->file/net/dbnet": { config_spec }
 55      }
 56    }
 57    ```
 58    """
 59
 60    ############################
 61    def __init__(self):
 62        # Called when we update which configs are active.
 63        self.update_callbacks = []
 64
 65        # Called when the set of configs changes.
 66        self.load_callbacks = []
 67
 68        # Called, obviously, when 'quit' is signalled.
 69        self.quit_callbacks = []
 70
 71    #############################
 72    # API methods below are used in querying/modifying the API for the
 73    # record of the running state of loggers.
 74    #############################
 75    # def get_cruises(self):
 76    #   """Return list of cruise id's. Returns, e.g.
 77    #   > api.get_cruises()
 78    #       ["NBP1700", "NBP1701"]
 79    #   """
 80    #   raise NotImplementedError('get_cruises must be implemented by subclass')
 81
 82    #############################
 83    def get_configuration(self):
 84        """Get OpenRVDAS configuration from the data store.
 85        """
 86        raise NotImplementedError('get_configuration must be implemented by subclass')
 87
 88    #############################
 89    def get_modes(self):
 90        """Get the list of modes from the data store.
 91        > api.get_modes()
 92            ["off", "port", "underway"]
 93        """
 94        raise NotImplementedError('get_modes must be implemented by subclass')
 95
 96    #############################
 97    def get_active_mode(self):
 98        """Get the currently active mode from the data store.
 99        > api.get_active_mode()
100            "port"
101        """
102        raise NotImplementedError('get_active_mode must be implemented by subclass')
103
104    #############################
105    def get_default_mode(self):
106        """Get the default mode from the data store.
107        > api.get_default_mode()
108            "off"
109        """
110        raise NotImplementedError('get_default_mode must be implemented by subclass')
111
112    #############################
113    def get_loggers(self):
114        """Get the dict of {logger_id:logger_spec,...} from the data store.
115        > api.get_loggers()
116            {
117              "knud": {"host": "knud.pi", "configs":...},
118              "gyr1": {"configs":...}
119            }
120        """
121        raise NotImplementedError('get_loggers must be implemented by subclass')
122
123    #############################
124    def get_logger(self, logger_id):
125        """Retrieve the logger spec for the specified logger id.
126        > api.get_logger('knud')
127            {"name": "knud->net", "host_id": "knud.pi", "configs":...}
128        """
129        raise NotImplementedError('get_logger must be implemented by subclass')
130
131    #############################
132    def get_logger_config(self, config_name):
133        """Retrieve the logger config associated with the specified name.
134        > api.get_logger_config('knud->net')
135               { "readers": [...], "transforms": [...], "writers": [...] }
136        """
137        raise NotImplementedError('get_logger_config must be implemented by subclass')
138
139    #############################
140    def get_logger_configs(self, mode=None):
141        """Retrieve the configs associated with a mode from the data store.
142        If mode is omitted, retrieve configs associated with the active mode.
143        > api.get_logger_configs()
144               {"knud": { config_spec },
145                "gyr1": { config_spec }
146               }
147        """
148        raise NotImplementedError('get_logger_configs must be implemented by subclass')
149
150    #############################
151    def get_logger_config_name(self, logger_id, mode=None):
152        """Retrieve the name of the logger config associated with the
153        specified logger in the specified mode. If mode is omitted,
154        retrieve config name associated with the active mode.
155        > api.get_logger_config_name('knud')
156            knud->net
157       """
158        raise NotImplementedError(
159            'get_logger_config_name must be implemented by subclass')
160
161    #############################
162    def get_logger_config_names(self, logger_id):
163        """Retrieve list of logger config names for the specified logger.
164        > api.get_logger_config_names('knud')
165            ["off", "knud->net", "knud->net/file", "knud->net/file/db"]
166        """
167        raise NotImplementedError(
168            'get_logger_config_names must be implemented by subclass')
169
170    ############################
171    # Methods for manipulating the desired state via API to indicate
172    # current mode and which loggers should be in which configs.
173    #
174    # These are triggered from the user/API/web interface
175    ############################
176    def set_active_mode(self, mode):
177        """Set the active mode for OpenRVDAS.
178        > api.set_active_mode(port')
179        """
180        raise NotImplementedError('set_active_mode must be implemented by subclass')
181
182    #############################
183    def set_active_logger_config(self, logger, config_name):
184        """Set the active logger config for the specified logger to
185        the specific logger_config name.
186        > api.set_active_logger_config('knud', 'knud->file/net/db')
187        """
188        raise NotImplementedError(
189            'set_active_logger_config must be implemented by subclass')
190
191    #############################
192
193    def quit(self):
194        """Execute any callbacks that were registered to run on quit."""
195        for (callback, kwargs) in self.quit_callbacks:
196            logging.debug('Executing quit callback: %s', callback)
197            callback(**kwargs)
198
199    #############################
200    # API method to register a callback. When the data store changes,
201    # methods that are registered via on_update() will be called so they
202    # can fetch updated results.
203    #############################
204    def on_update(self, callback, kwargs=None):
205        """Register a method to be called when current configs change."""
206        if kwargs is None:
207            kwargs = {}
208        self.update_callbacks.append((callback, kwargs))
209
210    #############################
211    def signal_update(self):
212        """Call the registered methods when current configs change."""
213        for (callback, kwargs) in self.update_callbacks:
214            logging.debug('Executing update callback: %s', callback)
215            callback(**kwargs)
216
217    #############################
218    # API method to register a callback. When the data store changes,
219    # methods that are registered via on_update() will be called so they
220    # can fetch updated results.
221    #############################
222    def on_load(self, callback, kwargs=None):
223        """Register a method to be called when new configs have been loaded."""
224        if kwargs is None:
225            kwargs = {}
226        self.load_callbacks.append((callback, kwargs))
227
228    #############################
229    def signal_load(self):
230        """Call the registered methods when new configs have been loaded."""
231        for (callback, kwargs) in self.load_callbacks:
232            logging.debug('Executing load callback: %s', callback)
233            callback(**kwargs)
234
235    #############################
236    # API method to register a callback. When the data store changes,
237    # methods that are registered via on_update() will be called so they
238    # can fetch updated results.
239    #############################
240    def on_quit(self, callback, kwargs=None):
241        """Register a method to be called when quit is signaled changes."""
242        if kwargs is None:
243            kwargs = {}
244        self.quit_callbacks.append((callback, kwargs))
245
246    ############################
247    # Methods for getting logger status data from API
248    ############################
249    def get_status(self, since_timestamp=None):
250        """Retrieve a dict of the most-recent status report from each
251        logger. If since_timestamp is specified, retrieve all status reports
252        since that time."""
253        raise NotImplementedError('get_status must be implemented by subclass')
254
255    ############################
256    # Methods for storing/retrieving messages from servers/loggers/etc.
257    ############################
258    # Logging levels corresponding to logging module levels
259    CRITICAL = logging.CRITICAL
260    ERROR = logging.ERROR
261    WARNING = logging.WARNING
262    INFO = logging.INFO
263    DEBUG = logging.DEBUG
264
265    ############################
266    def message_log(self, source, user, log_level, message):
267        """Timestamp and store the passed message."""
268        raise NotImplementedError('message_log must be implemented by subclass')
269
270    ############################
271    def get_message_log(self, source=None, user=None, log_level=sys.maxsize,
272                        since_timestamp=None):
273        """Retrieve log messages from source at or above log_level since
274        timestamp. If source is omitted, retrieve from all sources. If
275        log_level is omitted, retrieve at all levels. If since_timestamp is
276        omitted, only retrieve most recent message.
277        """
278        raise NotImplementedError('get_message_log must be implemented by subclass')
279
280    #############################
281    """Methods below are used to load/create/modify the data store's model
282  of a cruise."""
283    #############################
284
285    def load_configuration(self, configuration):
286        """Load a complete cruise configuration to the data store.
287        > api.load_configuration({ configuration })
288        """
289        raise NotImplementedError('load_configuration must be implemented by subclass')
290
291    #############################
292    # def add_cruise(self, cruise_id, start=None, end=None):
293    #   """Add a new cruise_id to the data store. Use methods below to build
294    #   it out.
295    #   > api.add_cruise('NBP1702', '2017-02-02', '2017-03-01')
296    #   """
297    #   raise NotImplementedError('add_cruise must be implemented by subclass')
298
299    #############################
300    def delete_configuration(self):
301        """Remove the specified cruise from the data store.
302        > api.delete_configuration()
303        """
304        raise NotImplementedError('delete_configuration must be implemented by subclass')
305
306    #############################
307    def add_mode(self, mode):
308        """Add a new mode to the OpenRVDAS configuration.
309        > api.add_mode('underway')
310        """
311        raise NotImplementedError('add_mode must be implemented by subclass')
312
313    #############################
314    def delete_mode(self, mode):
315        """Delete the named mode (and all its configs) from the
316        data store. If the deleted mode is the active mode, set
317        the active mode to the default mode.
318        > api.delete_mode('underway')
319        """
320        raise NotImplementedError('delete_mode must be implemented by subclass')
321
322    #############################
323    def add_logger(self, logger_id, logger_config):
324        """Add a new logger to the data store.
325
326        logger_config - a dict defining:
327          host - optional restriction on which host logger must run
328          configs - list of logger_config names
329        > api.add_logger(gyr2', { 'host_id': <host_id>, 'configs': [....] })
330        """
331        raise NotImplementedError('add_logger must be implemented by subclass')
332
333    #############################
334    def delete_logger(self, logger_id):
335        """Remove a logger and all its associated logger_configs from the data store.
336        > api.delete_logger(gyr2')
337        """
338        raise NotImplementedError('delete_logger must be implemented by subclass')
339
340    #############################
341    def add_logger_config(self, logger_config_name, logger_config_spec):
342        """Add a new logger config to the data store.
343        > api.add_logger_config('gyr2->net/file/db', { logger_config_spec })
344        """
345        raise NotImplementedError('add_config must be implemented by subclass')
346
347    #############################
348    def add_logger_config_to_logger(self, config, logger_id):
349        """Associate a config with a logger.
350        > api.add_logger_config_to_logger('gyr2->net/file/db', 'gyr2')
351        """
352        raise NotImplementedError('add_logger_config_to_logger must be implemented by subclass')
353
354    #############################
355    def add_logger_config_to_mode(self, config, logger_id, mode):
356        """Associate a config with a logger and mode.
357        > api.add_logger_config_to_mode('gyr2->net/file/db', 'gyr2', 'underway')
358        """
359        raise NotImplementedError('add_logger_config_to_mode must be implemented by subclass')
360
361    #############################
362    def delete_logger_config(self, config_id):
363        """Delete specified config from data store (and by extension,
364        from the mode and logger with which it is associated.
365        > api.delete_logger_config('gyr2->net/file/db')
366        """
367        raise NotImplementedError('delete_logger_config must be implemented by subclass')
class ServerAPI:
 11class ServerAPI:
 12    """Abstract base class defining an API through which a LoggerServer
 13    can interact with a data store.
 14
 15    Parameters below have the following semantics:
 16    ```
 17    configuration - dict definition of a OpenRVDAS configuration
 18    mode          - dict name of mode, and logger_config_names associated with that mode
 19    default_mode  - name of mode to use at startup or when returning to a default state
 20
 21    logger_config_name - string name of a logger configuration, unique within configration
 22    logger_config - dict definition of a logger configuration
 23
 24    logger_id     - string name of logger, unique within cruise
 25    logger        - dict definition of logger, including list of names of
 26                    valid configs and optional host restriction
 27
 28    logger_configs - dict of {logger_config_name:logger_config,...}
 29    ```
 30    For the purposes of documentation below, assume a sample
 31    cruise_config as follows:
 32    ```
 33    {
 34      "loggers": {
 35        "knud": {
 36          "host": "knud.pi",
 37          "configs": ["off", "knud->net", "knud->file/net/db"]
 38        },
 39        "gyr1": {
 40          "configs": ["off", "gyr1->net", "gyr1->file/net/db"]
 41        },
 42      "modes": {
 43        "off": {"knud": "off", "gyr1": "off"},
 44        "port": {"knud": "off", "gyr1": "gyr1->net"},
 45        "underway": { "knud": "knud->file/net/db",
 46                      "gyr1": "gyr1->file/net/db"
 47                    }
 48      },
 49      "default_mode": "off",
 50      "configs": {
 51        "off": {},
 52        "knud->net": { config_spec },
 53        "knud->file/net/db": { config_spec },
 54        "gyr1->net": { config_spec },
 55        "gyr1->file/net/dbnet": { config_spec }
 56      }
 57    }
 58    ```
 59    """
 60
 61    ############################
 62    def __init__(self):
 63        # Called when we update which configs are active.
 64        self.update_callbacks = []
 65
 66        # Called when the set of configs changes.
 67        self.load_callbacks = []
 68
 69        # Called, obviously, when 'quit' is signalled.
 70        self.quit_callbacks = []
 71
 72    #############################
 73    # API methods below are used in querying/modifying the API for the
 74    # record of the running state of loggers.
 75    #############################
 76    # def get_cruises(self):
 77    #   """Return list of cruise id's. Returns, e.g.
 78    #   > api.get_cruises()
 79    #       ["NBP1700", "NBP1701"]
 80    #   """
 81    #   raise NotImplementedError('get_cruises must be implemented by subclass')
 82
 83    #############################
 84    def get_configuration(self):
 85        """Get OpenRVDAS configuration from the data store.
 86        """
 87        raise NotImplementedError('get_configuration must be implemented by subclass')
 88
 89    #############################
 90    def get_modes(self):
 91        """Get the list of modes from the data store.
 92        > api.get_modes()
 93            ["off", "port", "underway"]
 94        """
 95        raise NotImplementedError('get_modes must be implemented by subclass')
 96
 97    #############################
 98    def get_active_mode(self):
 99        """Get the currently active mode from the data store.
100        > api.get_active_mode()
101            "port"
102        """
103        raise NotImplementedError('get_active_mode must be implemented by subclass')
104
105    #############################
106    def get_default_mode(self):
107        """Get the default mode from the data store.
108        > api.get_default_mode()
109            "off"
110        """
111        raise NotImplementedError('get_default_mode must be implemented by subclass')
112
113    #############################
114    def get_loggers(self):
115        """Get the dict of {logger_id:logger_spec,...} from the data store.
116        > api.get_loggers()
117            {
118              "knud": {"host": "knud.pi", "configs":...},
119              "gyr1": {"configs":...}
120            }
121        """
122        raise NotImplementedError('get_loggers must be implemented by subclass')
123
124    #############################
125    def get_logger(self, logger_id):
126        """Retrieve the logger spec for the specified logger id.
127        > api.get_logger('knud')
128            {"name": "knud->net", "host_id": "knud.pi", "configs":...}
129        """
130        raise NotImplementedError('get_logger must be implemented by subclass')
131
132    #############################
133    def get_logger_config(self, config_name):
134        """Retrieve the logger config associated with the specified name.
135        > api.get_logger_config('knud->net')
136               { "readers": [...], "transforms": [...], "writers": [...] }
137        """
138        raise NotImplementedError('get_logger_config must be implemented by subclass')
139
140    #############################
141    def get_logger_configs(self, mode=None):
142        """Retrieve the configs associated with a mode from the data store.
143        If mode is omitted, retrieve configs associated with the active mode.
144        > api.get_logger_configs()
145               {"knud": { config_spec },
146                "gyr1": { config_spec }
147               }
148        """
149        raise NotImplementedError('get_logger_configs must be implemented by subclass')
150
151    #############################
152    def get_logger_config_name(self, logger_id, mode=None):
153        """Retrieve the name of the logger config associated with the
154        specified logger in the specified mode. If mode is omitted,
155        retrieve config name associated with the active mode.
156        > api.get_logger_config_name('knud')
157            knud->net
158       """
159        raise NotImplementedError(
160            'get_logger_config_name must be implemented by subclass')
161
162    #############################
163    def get_logger_config_names(self, logger_id):
164        """Retrieve list of logger config names for the specified logger.
165        > api.get_logger_config_names('knud')
166            ["off", "knud->net", "knud->net/file", "knud->net/file/db"]
167        """
168        raise NotImplementedError(
169            'get_logger_config_names must be implemented by subclass')
170
171    ############################
172    # Methods for manipulating the desired state via API to indicate
173    # current mode and which loggers should be in which configs.
174    #
175    # These are triggered from the user/API/web interface
176    ############################
177    def set_active_mode(self, mode):
178        """Set the active mode for OpenRVDAS.
179        > api.set_active_mode(port')
180        """
181        raise NotImplementedError('set_active_mode must be implemented by subclass')
182
183    #############################
184    def set_active_logger_config(self, logger, config_name):
185        """Set the active logger config for the specified logger to
186        the specific logger_config name.
187        > api.set_active_logger_config('knud', 'knud->file/net/db')
188        """
189        raise NotImplementedError(
190            'set_active_logger_config must be implemented by subclass')
191
192    #############################
193
194    def quit(self):
195        """Execute any callbacks that were registered to run on quit."""
196        for (callback, kwargs) in self.quit_callbacks:
197            logging.debug('Executing quit callback: %s', callback)
198            callback(**kwargs)
199
200    #############################
201    # API method to register a callback. When the data store changes,
202    # methods that are registered via on_update() will be called so they
203    # can fetch updated results.
204    #############################
205    def on_update(self, callback, kwargs=None):
206        """Register a method to be called when current configs change."""
207        if kwargs is None:
208            kwargs = {}
209        self.update_callbacks.append((callback, kwargs))
210
211    #############################
212    def signal_update(self):
213        """Call the registered methods when current configs change."""
214        for (callback, kwargs) in self.update_callbacks:
215            logging.debug('Executing update callback: %s', callback)
216            callback(**kwargs)
217
218    #############################
219    # API method to register a callback. When the data store changes,
220    # methods that are registered via on_update() will be called so they
221    # can fetch updated results.
222    #############################
223    def on_load(self, callback, kwargs=None):
224        """Register a method to be called when new configs have been loaded."""
225        if kwargs is None:
226            kwargs = {}
227        self.load_callbacks.append((callback, kwargs))
228
229    #############################
230    def signal_load(self):
231        """Call the registered methods when new configs have been loaded."""
232        for (callback, kwargs) in self.load_callbacks:
233            logging.debug('Executing load callback: %s', callback)
234            callback(**kwargs)
235
236    #############################
237    # API method to register a callback. When the data store changes,
238    # methods that are registered via on_update() will be called so they
239    # can fetch updated results.
240    #############################
241    def on_quit(self, callback, kwargs=None):
242        """Register a method to be called when quit is signaled changes."""
243        if kwargs is None:
244            kwargs = {}
245        self.quit_callbacks.append((callback, kwargs))
246
247    ############################
248    # Methods for getting logger status data from API
249    ############################
250    def get_status(self, since_timestamp=None):
251        """Retrieve a dict of the most-recent status report from each
252        logger. If since_timestamp is specified, retrieve all status reports
253        since that time."""
254        raise NotImplementedError('get_status must be implemented by subclass')
255
256    ############################
257    # Methods for storing/retrieving messages from servers/loggers/etc.
258    ############################
259    # Logging levels corresponding to logging module levels
260    CRITICAL = logging.CRITICAL
261    ERROR = logging.ERROR
262    WARNING = logging.WARNING
263    INFO = logging.INFO
264    DEBUG = logging.DEBUG
265
266    ############################
267    def message_log(self, source, user, log_level, message):
268        """Timestamp and store the passed message."""
269        raise NotImplementedError('message_log must be implemented by subclass')
270
271    ############################
272    def get_message_log(self, source=None, user=None, log_level=sys.maxsize,
273                        since_timestamp=None):
274        """Retrieve log messages from source at or above log_level since
275        timestamp. If source is omitted, retrieve from all sources. If
276        log_level is omitted, retrieve at all levels. If since_timestamp is
277        omitted, only retrieve most recent message.
278        """
279        raise NotImplementedError('get_message_log must be implemented by subclass')
280
281    #############################
282    """Methods below are used to load/create/modify the data store's model
283  of a cruise."""
284    #############################
285
286    def load_configuration(self, configuration):
287        """Load a complete cruise configuration to the data store.
288        > api.load_configuration({ configuration })
289        """
290        raise NotImplementedError('load_configuration must be implemented by subclass')
291
292    #############################
293    # def add_cruise(self, cruise_id, start=None, end=None):
294    #   """Add a new cruise_id to the data store. Use methods below to build
295    #   it out.
296    #   > api.add_cruise('NBP1702', '2017-02-02', '2017-03-01')
297    #   """
298    #   raise NotImplementedError('add_cruise must be implemented by subclass')
299
300    #############################
301    def delete_configuration(self):
302        """Remove the specified cruise from the data store.
303        > api.delete_configuration()
304        """
305        raise NotImplementedError('delete_configuration must be implemented by subclass')
306
307    #############################
308    def add_mode(self, mode):
309        """Add a new mode to the OpenRVDAS configuration.
310        > api.add_mode('underway')
311        """
312        raise NotImplementedError('add_mode must be implemented by subclass')
313
314    #############################
315    def delete_mode(self, mode):
316        """Delete the named mode (and all its configs) from the
317        data store. If the deleted mode is the active mode, set
318        the active mode to the default mode.
319        > api.delete_mode('underway')
320        """
321        raise NotImplementedError('delete_mode must be implemented by subclass')
322
323    #############################
324    def add_logger(self, logger_id, logger_config):
325        """Add a new logger to the data store.
326
327        logger_config - a dict defining:
328          host - optional restriction on which host logger must run
329          configs - list of logger_config names
330        > api.add_logger(gyr2', { 'host_id': <host_id>, 'configs': [....] })
331        """
332        raise NotImplementedError('add_logger must be implemented by subclass')
333
334    #############################
335    def delete_logger(self, logger_id):
336        """Remove a logger and all its associated logger_configs from the data store.
337        > api.delete_logger(gyr2')
338        """
339        raise NotImplementedError('delete_logger must be implemented by subclass')
340
341    #############################
342    def add_logger_config(self, logger_config_name, logger_config_spec):
343        """Add a new logger config to the data store.
344        > api.add_logger_config('gyr2->net/file/db', { logger_config_spec })
345        """
346        raise NotImplementedError('add_config must be implemented by subclass')
347
348    #############################
349    def add_logger_config_to_logger(self, config, logger_id):
350        """Associate a config with a logger.
351        > api.add_logger_config_to_logger('gyr2->net/file/db', 'gyr2')
352        """
353        raise NotImplementedError('add_logger_config_to_logger must be implemented by subclass')
354
355    #############################
356    def add_logger_config_to_mode(self, config, logger_id, mode):
357        """Associate a config with a logger and mode.
358        > api.add_logger_config_to_mode('gyr2->net/file/db', 'gyr2', 'underway')
359        """
360        raise NotImplementedError('add_logger_config_to_mode must be implemented by subclass')
361
362    #############################
363    def delete_logger_config(self, config_id):
364        """Delete specified config from data store (and by extension,
365        from the mode and logger with which it is associated.
366        > api.delete_logger_config('gyr2->net/file/db')
367        """
368        raise NotImplementedError('delete_logger_config must be implemented by subclass')

Abstract base class defining an API through which a LoggerServer can interact with a data store.

Parameters below have the following semantics:

configuration - dict definition of a OpenRVDAS configuration
mode          - dict name of mode, and logger_config_names associated with that mode
default_mode  - name of mode to use at startup or when returning to a default state

logger_config_name - string name of a logger configuration, unique within configration
logger_config - dict definition of a logger configuration

logger_id     - string name of logger, unique within cruise
logger        - dict definition of logger, including list of names of
                valid configs and optional host restriction

logger_configs - dict of {logger_config_name:logger_config,...}

For the purposes of documentation below, assume a sample cruise_config as follows:

{
  "loggers": {
    "knud": {
      "host": "knud.pi",
      "configs": ["off", "knud->net", "knud->file/net/db"]
    },
    "gyr1": {
      "configs": ["off", "gyr1->net", "gyr1->file/net/db"]
    },
  "modes": {
    "off": {"knud": "off", "gyr1": "off"},
    "port": {"knud": "off", "gyr1": "gyr1->net"},
    "underway": { "knud": "knud->file/net/db",
                  "gyr1": "gyr1->file/net/db"
                }
  },
  "default_mode": "off",
  "configs": {
    "off": {},
    "knud->net": { config_spec },
    "knud->file/net/db": { config_spec },
    "gyr1->net": { config_spec },
    "gyr1->file/net/dbnet": { config_spec }
  }
}
update_callbacks
load_callbacks
quit_callbacks
def get_configuration(self):
84    def get_configuration(self):
85        """Get OpenRVDAS configuration from the data store.
86        """
87        raise NotImplementedError('get_configuration must be implemented by subclass')

Get OpenRVDAS configuration from the data store.

def get_modes(self):
90    def get_modes(self):
91        """Get the list of modes from the data store.
92        > api.get_modes()
93            ["off", "port", "underway"]
94        """
95        raise NotImplementedError('get_modes must be implemented by subclass')

Get the list of modes from the data store.

api.get_modes() ["off", "port", "underway"]

def get_active_mode(self):
 98    def get_active_mode(self):
 99        """Get the currently active mode from the data store.
100        > api.get_active_mode()
101            "port"
102        """
103        raise NotImplementedError('get_active_mode must be implemented by subclass')

Get the currently active mode from the data store.

api.get_active_mode() "port"

def get_default_mode(self):
106    def get_default_mode(self):
107        """Get the default mode from the data store.
108        > api.get_default_mode()
109            "off"
110        """
111        raise NotImplementedError('get_default_mode must be implemented by subclass')

Get the default mode from the data store.

api.get_default_mode() "off"

def get_loggers(self):
114    def get_loggers(self):
115        """Get the dict of {logger_id:logger_spec,...} from the data store.
116        > api.get_loggers()
117            {
118              "knud": {"host": "knud.pi", "configs":...},
119              "gyr1": {"configs":...}
120            }
121        """
122        raise NotImplementedError('get_loggers must be implemented by subclass')

Get the dict of {logger_id:logger_spec,...} from the data store.

api.get_loggers() { "knud": {"host": "knud.pi", "configs":...}, "gyr1": {"configs":...} }

def get_logger(self, logger_id):
125    def get_logger(self, logger_id):
126        """Retrieve the logger spec for the specified logger id.
127        > api.get_logger('knud')
128            {"name": "knud->net", "host_id": "knud.pi", "configs":...}
129        """
130        raise NotImplementedError('get_logger must be implemented by subclass')

Retrieve the logger spec for the specified logger id.

api.get_logger('knud') {"name": "knud->net", "host_id": "knud.pi", "configs":...}

def get_logger_config(self, config_name):
133    def get_logger_config(self, config_name):
134        """Retrieve the logger config associated with the specified name.
135        > api.get_logger_config('knud->net')
136               { "readers": [...], "transforms": [...], "writers": [...] }
137        """
138        raise NotImplementedError('get_logger_config must be implemented by subclass')

Retrieve the logger config associated with the specified name.

api.get_logger_config('knud->net') { "readers": [...], "transforms": [...], "writers": [...] }

def get_logger_configs(self, mode=None):
141    def get_logger_configs(self, mode=None):
142        """Retrieve the configs associated with a mode from the data store.
143        If mode is omitted, retrieve configs associated with the active mode.
144        > api.get_logger_configs()
145               {"knud": { config_spec },
146                "gyr1": { config_spec }
147               }
148        """
149        raise NotImplementedError('get_logger_configs must be implemented by subclass')

Retrieve the configs associated with a mode from the data store. If mode is omitted, retrieve configs associated with the active mode.

api.get_logger_configs() {"knud": { config_spec }, "gyr1": { config_spec } }

def get_logger_config_name(self, logger_id, mode=None):
152    def get_logger_config_name(self, logger_id, mode=None):
153        """Retrieve the name of the logger config associated with the
154        specified logger in the specified mode. If mode is omitted,
155        retrieve config name associated with the active mode.
156        > api.get_logger_config_name('knud')
157            knud->net
158       """
159        raise NotImplementedError(
160            'get_logger_config_name must be implemented by subclass')

Retrieve the name of the logger config associated with the specified logger in the specified mode. If mode is omitted, retrieve config name associated with the active mode.

api.get_logger_config_name('knud') knud->net

def get_logger_config_names(self, logger_id):
163    def get_logger_config_names(self, logger_id):
164        """Retrieve list of logger config names for the specified logger.
165        > api.get_logger_config_names('knud')
166            ["off", "knud->net", "knud->net/file", "knud->net/file/db"]
167        """
168        raise NotImplementedError(
169            'get_logger_config_names must be implemented by subclass')

Retrieve list of logger config names for the specified logger.

api.get_logger_config_names('knud') ["off", "knud->net", "knud->net/file", "knud->net/file/db"]

def set_active_mode(self, mode):
177    def set_active_mode(self, mode):
178        """Set the active mode for OpenRVDAS.
179        > api.set_active_mode(port')
180        """
181        raise NotImplementedError('set_active_mode must be implemented by subclass')

Set the active mode for OpenRVDAS.

api.set_active_mode(port')

def set_active_logger_config(self, logger, config_name):
184    def set_active_logger_config(self, logger, config_name):
185        """Set the active logger config for the specified logger to
186        the specific logger_config name.
187        > api.set_active_logger_config('knud', 'knud->file/net/db')
188        """
189        raise NotImplementedError(
190            'set_active_logger_config must be implemented by subclass')

Set the active logger config for the specified logger to the specific logger_config name.

api.set_active_logger_config('knud', 'knud->file/net/db')

def quit(self):
194    def quit(self):
195        """Execute any callbacks that were registered to run on quit."""
196        for (callback, kwargs) in self.quit_callbacks:
197            logging.debug('Executing quit callback: %s', callback)
198            callback(**kwargs)

Execute any callbacks that were registered to run on quit.

def on_update(self, callback, kwargs=None):
205    def on_update(self, callback, kwargs=None):
206        """Register a method to be called when current configs change."""
207        if kwargs is None:
208            kwargs = {}
209        self.update_callbacks.append((callback, kwargs))

Register a method to be called when current configs change.

def signal_update(self):
212    def signal_update(self):
213        """Call the registered methods when current configs change."""
214        for (callback, kwargs) in self.update_callbacks:
215            logging.debug('Executing update callback: %s', callback)
216            callback(**kwargs)

Call the registered methods when current configs change.

def on_load(self, callback, kwargs=None):
223    def on_load(self, callback, kwargs=None):
224        """Register a method to be called when new configs have been loaded."""
225        if kwargs is None:
226            kwargs = {}
227        self.load_callbacks.append((callback, kwargs))

Register a method to be called when new configs have been loaded.

def signal_load(self):
230    def signal_load(self):
231        """Call the registered methods when new configs have been loaded."""
232        for (callback, kwargs) in self.load_callbacks:
233            logging.debug('Executing load callback: %s', callback)
234            callback(**kwargs)

Call the registered methods when new configs have been loaded.

def on_quit(self, callback, kwargs=None):
241    def on_quit(self, callback, kwargs=None):
242        """Register a method to be called when quit is signaled changes."""
243        if kwargs is None:
244            kwargs = {}
245        self.quit_callbacks.append((callback, kwargs))

Register a method to be called when quit is signaled changes.

def get_status(self, since_timestamp=None):
250    def get_status(self, since_timestamp=None):
251        """Retrieve a dict of the most-recent status report from each
252        logger. If since_timestamp is specified, retrieve all status reports
253        since that time."""
254        raise NotImplementedError('get_status must be implemented by subclass')

Retrieve a dict of the most-recent status report from each logger. If since_timestamp is specified, retrieve all status reports since that time.

CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
def message_log(self, source, user, log_level, message):
267    def message_log(self, source, user, log_level, message):
268        """Timestamp and store the passed message."""
269        raise NotImplementedError('message_log must be implemented by subclass')

Timestamp and store the passed message.

def get_message_log( self, source=None, user=None, log_level=9223372036854775807, since_timestamp=None):
272    def get_message_log(self, source=None, user=None, log_level=sys.maxsize,
273                        since_timestamp=None):
274        """Retrieve log messages from source at or above log_level since
275        timestamp. If source is omitted, retrieve from all sources. If
276        log_level is omitted, retrieve at all levels. If since_timestamp is
277        omitted, only retrieve most recent message.
278        """
279        raise NotImplementedError('get_message_log must be implemented by subclass')

Retrieve log messages from source at or above log_level since timestamp. If source is omitted, retrieve from all sources. If log_level is omitted, retrieve at all levels. If since_timestamp is omitted, only retrieve most recent message.

def load_configuration(self, configuration):
286    def load_configuration(self, configuration):
287        """Load a complete cruise configuration to the data store.
288        > api.load_configuration({ configuration })
289        """
290        raise NotImplementedError('load_configuration must be implemented by subclass')

Load a complete cruise configuration to the data store.

api.load_configuration({ configuration })

def delete_configuration(self):
301    def delete_configuration(self):
302        """Remove the specified cruise from the data store.
303        > api.delete_configuration()
304        """
305        raise NotImplementedError('delete_configuration must be implemented by subclass')

Remove the specified cruise from the data store.

api.delete_configuration()

def add_mode(self, mode):
308    def add_mode(self, mode):
309        """Add a new mode to the OpenRVDAS configuration.
310        > api.add_mode('underway')
311        """
312        raise NotImplementedError('add_mode must be implemented by subclass')

Add a new mode to the OpenRVDAS configuration.

api.add_mode('underway')

def delete_mode(self, mode):
315    def delete_mode(self, mode):
316        """Delete the named mode (and all its configs) from the
317        data store. If the deleted mode is the active mode, set
318        the active mode to the default mode.
319        > api.delete_mode('underway')
320        """
321        raise NotImplementedError('delete_mode must be implemented by subclass')

Delete the named mode (and all its configs) from the data store. If the deleted mode is the active mode, set the active mode to the default mode.

api.delete_mode('underway')

def add_logger(self, logger_id, logger_config):
324    def add_logger(self, logger_id, logger_config):
325        """Add a new logger to the data store.
326
327        logger_config - a dict defining:
328          host - optional restriction on which host logger must run
329          configs - list of logger_config names
330        > api.add_logger(gyr2', { 'host_id': <host_id>, 'configs': [....] })
331        """
332        raise NotImplementedError('add_logger must be implemented by subclass')

Add a new logger to the data store.

logger_config - a dict defining: host - optional restriction on which host logger must run configs - list of logger_config names

api.add_logger(gyr2', { 'host_id': , 'configs': [....] })

def delete_logger(self, logger_id):
335    def delete_logger(self, logger_id):
336        """Remove a logger and all its associated logger_configs from the data store.
337        > api.delete_logger(gyr2')
338        """
339        raise NotImplementedError('delete_logger must be implemented by subclass')

Remove a logger and all its associated logger_configs from the data store.

api.delete_logger(gyr2')

def add_logger_config(self, logger_config_name, logger_config_spec):
342    def add_logger_config(self, logger_config_name, logger_config_spec):
343        """Add a new logger config to the data store.
344        > api.add_logger_config('gyr2->net/file/db', { logger_config_spec })
345        """
346        raise NotImplementedError('add_config must be implemented by subclass')

Add a new logger config to the data store.

api.add_logger_config('gyr2->net/file/db', { logger_config_spec })

def add_logger_config_to_logger(self, config, logger_id):
349    def add_logger_config_to_logger(self, config, logger_id):
350        """Associate a config with a logger.
351        > api.add_logger_config_to_logger('gyr2->net/file/db', 'gyr2')
352        """
353        raise NotImplementedError('add_logger_config_to_logger must be implemented by subclass')

Associate a config with a logger.

api.add_logger_config_to_logger('gyr2->net/file/db', 'gyr2')

def add_logger_config_to_mode(self, config, logger_id, mode):
356    def add_logger_config_to_mode(self, config, logger_id, mode):
357        """Associate a config with a logger and mode.
358        > api.add_logger_config_to_mode('gyr2->net/file/db', 'gyr2', 'underway')
359        """
360        raise NotImplementedError('add_logger_config_to_mode must be implemented by subclass')

Associate a config with a logger and mode.

api.add_logger_config_to_mode('gyr2->net/file/db', 'gyr2', 'underway')

def delete_logger_config(self, config_id):
363    def delete_logger_config(self, config_id):
364        """Delete specified config from data store (and by extension,
365        from the mode and logger with which it is associated.
366        > api.delete_logger_config('gyr2->net/file/db')
367        """
368        raise NotImplementedError('delete_logger_config must be implemented by subclass')

Delete specified config from data store (and by extension, from the mode and logger with which it is associated.

api.delete_logger_config('gyr2->net/file/db')