def config_getter(context, key, parse=True, default=None): """Get an item from the config server by key name, using dotted notation (e.g. 'location.elevation') If no key is given, returns the entire config. """ host = context.obj.get('host') port = context.obj.get('port') try: # The nargs=-1 makes this a tuple so we get first entry. key = key[0] except IndexError: key = None logger.debug(f'Getting config key={key!r}') try: config_entry = get_config(key=key, host=host, port=port, parse=parse, default=default) except Exception as e: logger.error(f'Error while trying to get config: {e!r}') click.secho(f'Error while trying to get config: {e!r}', fg='red') else: logger.debug(f'Config server response: config_entry={config_entry!r}') click.echo(config_entry)
def __init__(self, msg=None, exit=False): self.msg = msg if self.msg: logger.error(str(self)) if exit: self.exit_program(self.msg)
def write_fits(data, header, filename, exposure_event=None, **kwargs): """Write FITS file to requested location. >>> from panoptes.utils.images import fits as fits_utils >>> data = np.random.normal(size=100) >>> header = { 'FILE': 'delete_me', 'TEST': True } >>> filename = str(getfixture('tmpdir').join('temp.fits')) >>> fits_utils.write_fits(data, header, filename) >>> assert os.path.exists(filename) >>> fits_utils.getval(filename, 'FILE') 'delete_me' >>> data2 = fits_utils.getdata(filename) >>> assert np.array_equal(data, data2) Args: data (array_like): The data to be written. header (dict): Dictionary of items to be saved in header. filename (str): Path to filename for output. exposure_event (None|`threading.Event`, optional): A `threading.Event` that can be triggered when the image is written. kwargs (dict): Options that are passed to the `astropy.io.fits.PrimaryHDU.writeto` method. """ if not isinstance(header, fits.Header): header = fits.Header(header) hdu = fits.PrimaryHDU(data, header=header) # Create directories if required. if os.path.dirname(filename): os.makedirs(os.path.dirname(filename), mode=0o775, exist_ok=True) try: hdu.writeto(filename, **kwargs) except OSError as err: logger.error(f'Error writing image to {filename}: {err!r}') else: logger.debug(f'Image written to {filename}') finally: if exposure_event: exposure_event.set()
def run(context, config_file=None, save_local=True, load_local=False, heartbeat=True): """Runs the config server with command line options. This function is installed as an entry_point for the module, accessible at `panoptes-config-server`. """ host = context.obj.get('host') port = context.obj.get('port') server_process = server.config_server(config_file, host=host, port=port, load_local=load_local, save_local=save_local, auto_start=False) try: print(f'Starting config server. Ctrl-c to stop') server_process.start() print( f'Config server started on server_process.pid={server_process.pid!r}. ' f'Set "config_server.running=False" or Ctrl-c to stop') # Loop until config told to stop. while server_is_running(): time.sleep(heartbeat) server_process.terminate() server_process.join(30) except KeyboardInterrupt: logger.info( f'Config server interrupted, shutting down {server_process.pid}') server_process.terminate() except Exception as e: # pragma: no cover logger.error(f'Unable to start config server {e!r}')
def get_config_entry(): """Get config entries from server. Endpoint that responds to GET and POST requests and returns configuration item corresponding to provided key or entire configuration. The key entries should be specified in dot-notation, with the names corresponding to the entries stored in the configuration file. See the `scalpl <https://pypi.org/project/scalpl/>`_ documentation for details on the dot-notation. The endpoint should receive a JSON document with a single key named ``"key"`` and a value that corresponds to the desired key within the configuration. For example, take the following configuration: .. code:: javascript { 'location': { 'elevation': 3400.0, 'latitude': 19.55, 'longitude': 155.12, } } To get the corresponding value for the elevation, pass a JSON document similar to: .. code:: javascript '{"key": "location.elevation"}' Returns: str: The json string for the requested object if object is found in config. Otherwise a json string with ``status`` and ``msg`` keys will be returned. """ params = dict() if request.method == 'GET': params = request.args elif request.method == 'POST': params = request.get_json() verbose = params.get('verbose', True) log_level = 'DEBUG' if verbose else 'TRACE' # If requesting specific key logger.log(log_level, f'Received params={params!r}') if request.is_json: try: key = params['key'] logger.log(log_level, f'Request contains key={key!r}') except KeyError: return jsonify({ 'success': False, 'msg': "No valid key found. Need json request: {'key': <config_entry>}" }) if key is None: # Return all logger.log(log_level, 'No valid key given, returning entire config') show_config = app.config['POCS'] else: try: logger.log(log_level, f'Looking for key={key!r} in config') show_config = app.config['POCS_cut'].get(key, None) except Exception as e: logger.error(f'Error while getting config item: {e!r}') show_config = None else: # Return entire config logger.log(log_level, 'No valid key given, returning entire config') show_config = app.config['POCS'] logger.log(log_level, f'Returning show_config={show_config!r}') logger.log(log_level, f'Returning {show_config!r}') return jsonify(show_config)