def add_check_task(check, task): if not FirstTimeRun._validate_check_task(check): LOG.warning(_("Check %s was not of type method") % check) return if not FirstTimeRun._validate_check_task(task): LOG.warning(_("Task %s was not of type method") % task) return FirstTimeRun._check_tasks.append(FirstTimeRun.CheckTask(check, task))
def _run_tasks(self): """Will try to execute all calls from the internal list""" for task in self._tasks: try: task() LOG.info(_("Ran task: %s") % task) except Exception as e: LOG.error( _("Encountered error during first time" "initialization with task: %s") % e)
def _verify_transitions(self): """Iterate the TRANSITIONS dictionary and validate its completeness""" for t in self._sclass: if t not in self.transitions: raise RuntimeError( _("Not all states have required valid transition set")) for s in self.transitions[t]: if not isinstance(s, self._sclass): raise RuntimeError(_("Not all members of transition set " "are of same type as state"))
def test_translate_nl(self, m_environ, m_languages_get): m_languages_get.return_value = None m_environ.get.side_effect = [ 'nl', 'nl', 'nl', 'nl', 'radloggerpy/locale' ] m_translated_nl = _i18n._("RadLoggerPy opstarten met PID %s") m_untranslated = _i18n._("Starting RadLoggerPy service on PID %s") self.assertEqual(m_translated_nl, _i18n.translate(m_untranslated, 'nl_NL'))
def create_database(): """Create the database using sqlalchemy, used for first time init """ file = CONF.database.filename try: LOG.info(_("Creating database")) engine = create_engine(file) LOG.info(_("Creating database tables")) cd.create_database_tables(engine) except Exception as e: LOG.error(_("Failed to create database due to error: %s") % e) raise e
def _run_checks(self, all_to_init=False): """Run all checks from the internal list :param all_to_init: True if all checks are required to init False if one check is sufficient :return: True if first time init should be run False otherwise """ # store return values for all checks values = list() for check in self._checks: try: values.append(check()) except Exception as e: LOG.error( _("Encountered error while performing check for" "first time init: %s") % e) has_true = False for v in values: if v: has_true = True if v and not all_to_init: return True elif not v and all_to_init: return False return has_true
def main(): configurator.setup_config_and_logging(sys.argv, CONF) # Display logo's LOG.info(ascii_logo.TEXT + ascii_logo.LOGO) # Display pid LOG.info(_('Starting RadLoggerPy service on PID %s') % os.getpid()) # Perform first time initialization if required FirstTimeRun() # Create database session for main thread sess = database_manager.create_session() # launch device manager manager = DeviceManager() devices = SerialDeviceObject.find_enabled(sess) for device in devices: manager.launch_device(device) # TODO(Dantali0n): Improve state checking and error handling while True: manager.check_devices() time.sleep(30) # close all database sessions that are still left open database_manager.close_lingering_sessions()
def add_readings(self, readings): """Add the readings to the buffer Add all the readings to the buffer and remove any elements not of type :py:class: '~.RadiationReading'. :param readings: The readings to be added to the data buffer :type readings: List of :py:class: '~.RadiationReading' instances :return: True if the elements were successfully added False otherwise """ for e in readings: if not isinstance(e, RadiationReading): LOG.error( _("Element: %s, is not of type " "RadiationReading") % e) readings.remove(e) lock = self.rwlock.gen_rlock() try: if lock.acquire(): self.data.extend(readings) self.has_reading = True with self.condition: self.condition.notify() return True finally: lock.release() return False
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) if 'device' in args or 'name' in args: """Set device for MeasurementObject if any device params are set""" dev_obj = DeviceObject() if args['device']: dev_obj.id = args['device'] del args['device'] if args['name']: dev_obj.name = args['name'] del args['name'] args['device'] = dev_obj measure_obj = MeasurementObject(**args) data = MeasurementObject.find(self.app.database_session, measure_obj, True) if len(data) == 0: raise RuntimeWarning(_("No measurements found")) fields = ('timestamp', 'device', 'cpm', 'μSv/h') values = [] for result in data: value = (result.timestamp, result.device.id, result.cpm, result.svh) values.append(value) return [fields, values]
def __init__(self): """Run all checks and if required all initialization tasks""" if self._run_checks(): LOG.info(_("Performing first time initialization")) self._run_tasks() self._run_check_tasks()
def transition(self, state: enum.Enum): """Transition from the current state to a new desired state :param state: The new desired state :raises RuntimeWarning: This warning is raised when the new desired state requires an illegal transition """ if not isinstance(state, self._sclass): raise RuntimeWarning( _("State is not of same type as POSSIBLE_STATES")) if state in self.transitions[self._state]: self._state = state else: raise RuntimeWarning( _("Transition from %(initial)s to %(to)s state is not valid") % {'initial': self._state, 'to': state})
def _init(self): self.stop = False parity = PARITY_CHOICES_R[self.info.parity].value try: self.serial = serial.Serial(port=self.info.port, baudrate=self.info.baudrate, parity=parity, stopbits=self.info.stopbits, bytesize=self.info.bytesize) except serial.serialutil.SerialException as e: if e.errno == errno.EACCES: LOG.critical(_("Insufficient permissions " "to open device.")) raise DeviceException elif e.errno == errno.ENOENT: LOG.critical(_("Device does not exist")) raise DeviceException else: LOG.critical(_("Device error %d") % e.errno) raise DeviceException
def _run_check_tasks(self): """Run each of the checks and tasks as a pair""" for check_task in self._check_tasks: try: if check_task.check(): check_task.task() except Exception as e: LOG.error( _("Encountered error during execution of " "CheckTask: %s") % e)
def initialize_app(self, argv): # update configuration (sets CONF.version amongst others) parse_args(argv=()) # Display logo self.LOG.info(ascii_logo.TEXT + ascii_logo.LOGO) # Perform first time initialization if required FirstTimeRun() # Display version self.LOG.info(_('Initializing radloggercli %s') % CONF.version)
def set_cpm(self, cpm): """Set the counts per minute to the new value :param cpm: Counts per minute :type cpm: int """ if cpm < 0: LOG.warning(_("RadiationReading can not have negative cpm")) return self._cpm = cpm
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) device_obj = SerialDeviceObject(**args) try: data = SerialDeviceObject.find(self.app.database_session, device_obj, False) except MultipleResultsFound: raise RuntimeWarning(_("Multiple devices found")) if data is None: raise RuntimeWarning(_("Device could not be found")) fields = ('id', 'name', 'measurement type', 'interface', 'implementation', 'port', 'baudrate', 'bytesize', 'parity', 'stopbits', 'timeout') values = (data.id, data.name, data.type, data.interface, data.implementation, data.port, data.baudrate, data.bytesize, data.parity, data.stopbits, data.timeout) return (fields, values)
def check_database_missing(): """Check if the database is missing, used for first time init :return: True if the database does not exist False if it does """ file = CONF.database.filename LOG.info(_("Checking if database: %s exists") % file) if not os.path.isfile(file): LOG.warning(_("Database file does not exist in configured path")) return True try: engine = create_engine(file) if not database_exists(engine.url): return True except Exception as e: LOG.warning(e) return True return False
def __init__(self, transitions: _U, states: enum.Enum = None): if states and isinstance(states, enum.Enum): self.POSSIBLE_STATES = states elif not isinstance(self.POSSIBLE_STATES, enum.Enum): raise RuntimeError(_("Neither POSSIBLE_STATES nor states are of" "type Enum")) self._sclass = self.POSSIBLE_STATES.__class__ self.transitions = transitions self._verify_transitions() self.reset_state()
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) device_obj = DeviceObject(**args) if device_obj.id is None and device_obj.name is None: raise RuntimeWarning( _("Either the id or name must be specified to " "remove a device")) try: data = DeviceObject.delete(self.app.database_session, device_obj, False) except MultipleResultsFound: raise RuntimeWarning(_("Multiple devices found")) if data is None: raise RuntimeWarning(_("Device could not be found")) fields = ('id', 'name', 'interface', 'implementation') values = (data.id, data.name, data.interface, data.implementation) self.app.LOG.info(_("Device removed successfully")) return (fields, values)
def run(self): """Entry point for devices to initialize and start running Serves as the entry point for devices and calls _init and _run. In addition handles any required state transitions Any exception encountered will be raised so DeviceManager can handle it appropriately. """ if self._statemachine.get_state() is DeviceStates.ERROR: "Recover device from error state" LOG.info(_("Restarting {} device of implementation {} from " "previous error state.") .format(self.info.name, self.info.implementation)) self._statemachine.reset_state() elif self._statemachine.get_state() is not DeviceStates.STOPPED: "Not logging a message here, DeviceManager can easily do that" raise RuntimeError(_("Can not start same device {} multiple times") .format(self.info.name)) try: self._statemachine.transition(DeviceStates.INITIALIZING) self._init() except Exception: self._statemachine.transition(DeviceStates.ERROR) raise try: self._statemachine.transition(DeviceStates.RUNNING) self._run() except Exception: self._statemachine.transition(DeviceStates.ERROR) raise if self._statemachine.get_state() is DeviceStates.RUNNING: self._statemachine.transition(DeviceStates.STOPPED)
def create_session(): """Create a session using the appropriate configuration :return: Returns an sqlalchemy session or None if a error occurred :rtype: Instance of :py:class: 'orm.Session' """ file = CONF.database.filename try: sess = orm.sessionmaker(bind=create_engine(file)) return sess() except Exception as e: LOG.error(_("Failed to create session due to exception: %s") % e) return None
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) device_obj = DeviceObject(**args) details = args['detailed'] try: data = DeviceObject.find( self.app.database_session, device_obj, False) except MultipleResultsFound: raise RuntimeWarning(_("Multiple devices found")) if data is None: raise RuntimeWarning(_("Device could not be found")) fields = ( 'id', 'name', 'measurement type', 'interface', 'implementation') values = ( data.id, data.name, data.type, data.interface, data.implementation) if details and data.interface == \ INTERFACE_CHOICES[DeviceInterfaces.SERIAL]: data = SerialDeviceObject.find( self.app.database_session, device_obj, False) fields += ('port', 'baudrate', 'bytesize', 'parity', 'stopbits', 'timeout') values += (data.port, data.baudrate, data.bytesize, data.parity, data.stopbits, data.timeout) elif details and data.interface == \ INTERFACE_CHOICES[DeviceInterfaces.ETHERNET]: pass elif details and data.interface == \ INTERFACE_CHOICES[DeviceInterfaces.USB]: pass return (fields, values)
def get_data(self): """Return a collection of radiation monitoring data if any is available Retrieves the currently stored collection of radiation monitoring data and subsequently clears it. :return: Collection of RadiationReading objects :rtype: List of :py:class: '~.RadiationReading' instances """ got_data = self.data.fetch_clear_readings() if got_data: return got_data else: LOG.error(_("Unable to retrieve data for: %s") % self.NAME) return []
def __init__(self): num_workers = CONF.devices.concurrent_worker_amount if num_workers is -1: num_workers = multiprocessing.cpu_count() LOG.info( _("Configured device manager for %d workers") % num_workers) self._condition = Condition() self._mng_devices = [] "List of ManagedDevice devices see :py:class:`ManagedDevice`" self._threadpool = futurist.ThreadPoolExecutor(max_workers=num_workers) # self._threadpool = futurist.GreenThreadPoolExecutor( # max_workers=num_workers) self.get_device_map()
def _filter(filter_object, ignore=[]): """Filters the object depending on it's set attributes Removes certain empty objects such as empty collections but not empty strings or byte arrays. """ if ignore: LOG.warning( _("Use of deprecated ignore parameter on database " "object filter!")) return { key: name for (key, name) in vars(filter_object).items() if hasattr(filter_object.__class__, key) and ( key not in ignore or (seq_but_not_str(key and key))) }
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) device_obj = DeviceObject(**args) data = DeviceObject.find(self.app.database_session, device_obj, True) if len(data) == 0: raise RuntimeWarning(_("No devices found")) fields = ('id', 'name', 'measurement type', 'interface', 'implementation') values = [] for result in data: value = (result.id, result.name, result.type, result.interface, result.implementation) values.append(value) return [fields, values]
def find(session, reference, allow_multiple=True): reference._build_object() """Only look for serial devices""" reference.m_device.interface = DeviceInterfaces.SERIAL base_filters = reference._filter(reference.m_device) """Check if reference is base or child type when setting filters""" if hasattr(reference, 'm_serial_device'): filters = reference._filter(reference.m_serial_device) else: LOG.warning(_("Reference should be of type SerialDeviceObject")) filters = {} query = session.query(Device).filter_by(**base_filters)\ .join(SerialDevice).filter_by(**filters) if allow_multiple: results = query.all() if results is None: return None ret_results = list() for result in results: dev = SerialDeviceObject() dev.m_device = result dev.m_serial_device = result.serial[0] dev._build_attributes() ret_results.append(dev) return ret_results else: result = query.one_or_none() if result is None: return None dev = SerialDeviceObject() dev.m_device = result dev.m_serial_device = result.serial[0] dev._build_attributes() return dev
def take_action(self, parsed_args): args = dict(parsed_args._get_kwargs()) device_obj = SerialDeviceObject(**args) data = SerialDeviceObject.find( self.app.database_session, device_obj, True) if len(data) == 0: raise RuntimeWarning(_("No devices found")) fields = ( 'id', 'name', 'measurement type', 'interface', 'implementation', 'port', 'baudrate', 'bytesize', 'parity', 'stopbits', 'timeout') values = [] for result in data: value = (result.id, result.name, result.type, result.interface, result.implementation, result.port, result.baudrate, result.bytesize, result.parity, result.stopbits, result.timeout) values.append(value) return [fields, values]
def add(session, reference): reference._build_object() """Measurement.device_id must be set to populate the field""" if reference.m_measurement.device_id is None \ and hasattr(reference.device, 'id') and reference.device.id: """If no device_id is set find it through device id""" reference.m_measurement.device_id = reference.device.id elif reference.m_measurement.device_id is None and reference.device: """If no device_id find it through device""" dev = DeviceObject.find(session, reference.device, False) if dev is None: raise RuntimeError(_("No associateable Device found")) reference.m_measurement.device_id = dev.id session.add(reference.m_measurement) try: return session.commit() except Exception: session.rollback() # TODO(Dantali0n): These errors are horrendous for users to # understand an error abstraction is needed. raise
def check_devices(self): """Check the status of the devices and handle failures TODO(Dantali0n): This method should use the get_state method of devices instead of relying on the futures to much. """ removals = [] for mng_device in self._mng_devices: future_exception = mng_device.future.exception() if type(future_exception) is not DeviceException: LOG.error(_("Unhandled Exception")) if mng_device.future.done() and CONF.devices.restart_on_error: mng_device.future =\ self._threadpool.submit(mng_device.device.run) elif mng_device.future.done(): removals.append(mng_device) "Clean up the managed devices that have run to completion" for device in removals: self._mng_devices.remove(device)