def get_device(cls, name=None, device_class=None, connection_desc=None, connection_hops=None, initial_state=None, establish_connection=True): """ Return connection instance of given io_type/variant. :param name: name of device defined in configuration. :param device_class: 'moler.device.unixlocal.UnixLocal', 'moler.device.unixremote.UnixRemote', ... :param connection_desc: 'io_type' and 'variant' of device connection. :param connection_hops: connection hops to create device SM. :param initial_state: initial state for device e.g. UNIX_REMOTE. :param establish_connection: True to open connection, False if it does not matter. :return: requested device. """ if (not name) and (not device_class): raise WrongUsage( "Provide either 'name' or 'device_class' parameter (none given)" ) if name and device_class: raise WrongUsage( "Use either 'name' or 'device_class' parameter (not both)") with cls._lock_device: dev = cls._get_device_without_lock( name=name, device_class=device_class, connection_desc=connection_desc, connection_hops=connection_hops, initial_state=initial_state, establish_connection=establish_connection) return dev
def load_config(config=None, from_env_var=None, *args, **kwargs): """ Load Moler's configuration from config file. Load Moler's configuration from config file. :param config: either dict or config filename directly provided (overwrites 'from_env_var' if both given). :type config: dict or str :param from_env_var: name of environment variable storing config filename (file is in YAML format) :return: None """ global loaded_config add_devices_only = False from moler.device import DeviceFactory if config is not None and isinstance(config, dict): config = copy_dict(config, deep_copy=True) if "NOT_LOADED_YET" in loaded_config: loaded_config = [config] elif not DeviceFactory.was_any_device_deleted() and configs_are_same( config_list=loaded_config, config_to_find=config): return else: # Config was already loaded and now we have to add new devices. add_devices_only = True if not configs_are_same(config_list=loaded_config, config_to_find=config): loaded_config.append(config) wrong_type_config = not isinstance( config, six.string_types) and not isinstance(config, dict) if config is None and from_env_var is None: raise WrongUsage( "Provide either 'config' or 'from_env_var' parameter (none given)." ) elif (not from_env_var and wrong_type_config) or (config and wrong_type_config): raise WrongUsage( "Unsupported config type: '{}'. Allowed are: 'dict' or 'str' holding config filename (file is in YAML format)." .format(type(config))) if not config: if from_env_var not in os.environ: raise KeyError( "Environment variable '{}' is not set".format(from_env_var)) path = os.environ[from_env_var] config = read_yaml_configfile(path) elif isinstance(config, six.string_types): path = config config = read_yaml_configfile(path) # TODO: check schema if add_devices_only is False: load_logger_from_config(config) load_connection_from_config(config) load_device_from_config(config=config, add_only=add_devices_only)
def get_cloned_device(cls, source_device, new_name, initial_state=None, establish_connection=True, lazy_cmds_events=False): """ Creates (if necessary) and returns new device based on existed device. :param source_device: Reference to base device or name of base device. :param new_name: Name of new device. :param initial_state: Initial state of created device. If None then state of source device will be used. :param establish_connection: True to open connection, False if it does not matter. :return: Device object. """ with cls._lock_device: logger.info('START creating device {} from {}'.format( new_name, source_device)) source_device_name = source_device if isinstance(source_device, six.string_types): source_device = cls._get_device_without_lock( name=source_device, device_class=None, connection_desc=None, connection_hops=None, initial_state=None, establish_connection=True, lazy_cmds_events=lazy_cmds_events) logger.info('STEP 1 - creating source device {}'.format( source_device_name)) source_name = source_device.name # name already translated to alias. if new_name in cls._devices.keys(): cached_cloned_from = cls._devices_params[new_name][ 'cloned_from'] if cached_cloned_from == source_name: return cls._devices[new_name] else: msg = "Attempt to create device '{}' as clone of '{}' but device with such name already created " \ "as clone of '{}'.".format(new_name, source_name, cached_cloned_from) raise WrongUsage(msg) if initial_state is None: initial_state = source_device.current_state device_class = cls._devices_params[source_name]['class_fullname'] constructor_parameters = cls._devices_params[source_name][ 'constructor_parameters'] constructor_parameters["initial_state"] = initial_state if constructor_parameters["name"]: constructor_parameters["name"] = new_name logger.info('STEP 2 - creating cloned device {}'.format(new_name)) dev = cls._create_instance_and_remember_it( device_class=device_class, constructor_parameters=constructor_parameters, establish_connection=establish_connection, name=new_name) new_name = dev.name cls._devices_params[new_name]['cloned_from'] = source_name logger.info('DONE creating device {} from {}'.format( new_name, source_device_name)) return dev
def test_connection_observer_exception_do_not_remove(): ConnectionObserver.get_unraised_exceptions(True) time.sleep(0.1) from moler.cmd.unix.ls import Ls from moler.exceptions import CommandTimeout from moler.exceptions import WrongUsage cmd = Ls(None) none_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 0 == len(none_exceptions) cmd.set_exception(CommandTimeout(cmd, 0.1)) cmd._is_done = True active_exceptions = ConnectionObserver.get_unraised_exceptions(False) assert 1 == len(active_exceptions) cmd = Ls(None) ctoe = CommandTimeout(cmd, 0.1) cwue = WrongUsage(cmd, "Another exception") cmd.set_exception(ctoe) cmd._is_done = True cmd.set_exception(cwue) active_exceptions = ConnectionObserver.get_unraised_exceptions(False) assert ctoe == active_exceptions[1] assert 2 == len(active_exceptions) active_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 2 == len(active_exceptions) none_exceptions = ConnectionObserver.get_unraised_exceptions(True) assert 0 == len(none_exceptions)
def load_config(config=None, from_env_var=None, config_type='yaml'): """ Load Moler's configuration from config file :param config: either dict or config filename directly provided (overwrites 'from_env_var' if both given) :param from_env_var: name of environment variable storing config filename :param config_type: 'dict' ('config' param is dict) or 'yaml' ('config' is filename of file with YAML content) :return: None """ global loaded_config add_devices_only = False if "NOT_LOADED_YET" in loaded_config: loaded_config = [config] elif configs_are_same(config_list=loaded_config, config_to_find=config): return else: # Config was already loaded and now we have to add new devices. add_devices_only = True loaded_config.append(config) if (config_type != 'dict') and (config_type != 'yaml'): # no other format supported yet raise WrongUsage( "Unsupported config_type: '{}'. Allowed are: 'dict' or 'yaml'.". format(config_type)) if not config: if not from_env_var: raise WrongUsage( "Provide either 'config' or 'from_env_var' parameter (none given)." ) if from_env_var not in os.environ: raise KeyError( "Environment variable '{}' is not set".format(from_env_var)) path = os.environ[from_env_var] config = read_yaml_configfile(path) elif config_type == 'yaml': assert isinstance(config, six.string_types) path = config config = read_yaml_configfile(path) elif config_type == 'dict': assert isinstance(config, dict) # TODO: check schema if add_devices_only is False: load_logger_from_config(config) load_connection_from_config(config) load_device_from_config(config=config, add_only=add_devices_only)
def get_device(cls, name=None, device_class=None, connection_desc=None, connection_hops=None, initial_state=None, establish_connection=True, lazy_cmds_events=False, io_connection=None, additional_params=None): """ Return connection instance of given io_type/variant. :param name: name of device defined in configuration. :param device_class: 'moler.device.unixlocal.UnixLocal', 'moler.device.unixremote.UnixRemote', ... :param connection_desc: 'io_type' and 'variant' of device connection. Ignored if device already created or argument io_connection is passed. :param connection_hops: connection hops to create device SM. :param initial_state: initial state for device e.g. UNIX_REMOTE. :param establish_connection: True to open connection, False if it does not matter. :param lazy_cmds_events: set False to load all commands and events when device is initialized, set True to load commands and events when they are required for the first time. :param io_connection: connection for device. Ignored if device is already created. :param additional_params: dict with parameter(s) specific for the device. Will be passed to the constructor. If no specific parameter then set to None. :return: requested device. """ if (not name) and (not device_class): raise WrongUsage( "Provide either 'name' or 'device_class' parameter (none given)" ) if name and device_class: raise WrongUsage( "Use either 'name' or 'device_class' parameter (not both)") with cls._lock_device: dev = cls._get_device_without_lock( name=name, device_class=device_class, connection_desc=connection_desc, connection_hops=connection_hops, initial_state=initial_state, establish_connection=establish_connection, lazy_cmds_events=lazy_cmds_events, io_connection=io_connection, additional_params=additional_params) return dev
def _unknown_send(self, data2send): err_msg = "Can't send('{}')".format(data2send) err_msg += "\nYou haven't installed sending method of external-IO system" err_msg += "\n{}: {}(how2send=external_io_send)".format( "Do it either during connection construction", self.__class__.__name__) err_msg += "\nor later via attribute direct set: connection.how2send = external_io_send" raise WrongUsage(err_msg)
def __init__(self, selected_mode, connection=None, prompt=None, newline_chars=None, runner=None): """Create instance of SetMode class""" super(SetMode, self).__init__(connection, operation='execute', prompt=prompt, newline_chars=newline_chars, runner=runner) self.selected_mode = selected_mode.lower() if self.selected_mode not in SetMode.mode2cops_value: raise WrongUsage('\"{}\" is not correct mode. Available modes: {}.'.format( self.selected_mode, list(SetMode.mode2cops_value.keys()))) self.ret_required = False
def _get_parser(self): parsers = { "any": self._catch_any, "all": self._catch_all, "sequence": self._catch_sequence, } if self.match in parsers: return parsers[self.match] else: self.set_exception(WrongUsage("'{}' is not supported. Possible choices: 'any', 'all' or 'sequence'". format(self.match)))
def connection_closed_handler(self): """ Called by Moler (ThreadedMolerConnection) when connection is closed. :return: None """ if not self.done(): connection_name = self.get_logger_name() msg = "'{}' is not done but connection '{}' is about to be closed.".format( self, connection_name) ex = WrongUsage(msg) self.set_exception(ex) self.cancel()
def __init__(self, scheduler_type=None): """ :param scheduler_type: 'thread' or 'asyncio' """ with Scheduler._lock: if Scheduler._object: raise WrongUsage( "Scheduler object already created. Cannot create more than one instance." ) super(Scheduler, self).__init__() self._scheduler_type = None self._scheduler = None self._swap_scheduler(scheduler_type) Scheduler._object = self
def _create_scheduler(self, scheduler_type): """ :param scheduler_type: type of new scheduler: 'thread' or 'asyncio' :return: instance of scheduler """ if self._scheduler_type == scheduler_type: return self._scheduler if scheduler_type == 'thread': scheduler = MolerThreadScheduler() elif scheduler_type == 'asyncio': scheduler = MolerAsyncioScheduler() else: raise WrongUsage( "Wrong value of 'scheduler_type': '{}'. Allowed are 'thread' or 'asyncio'" .format(scheduler_type)) scheduler.start() return scheduler
def _raise_wrong_usage_of_wait_for(self, connection_observer): import inspect # don't import if never raising this exception (_, _, _, caller_name, _, _) = inspect.stack()[1] (_, _, _, caller_caller_name, _, _) = inspect.stack()[2] # Prefer to speak in observer API not runner API since user uses observers-API (runner is hidden) user_call_name = caller_caller_name if caller_caller_name == 'await_done' else caller_name err_msg = "Can't call {}() from 'async def' - it is blocking call".format( user_call_name) err_msg += "\n observer = {}()".format( connection_observer.__class__.__name__) err_msg += "\n observer.start()" err_msg += "\nconsider using:" err_msg += "\n await observer" err_msg += "\ninstead of:" err_msg += "\n observer.await_done()" self.logger.error(msg=err_msg) raise WrongUsage(err_msg)
def _is_device_creation_needed(name, requested_device_def): """ :param name: Name of device :param requested_device_def: Definition of device requested to create/ :return: True if device doesn't exist. False if device already exists. : """ from moler.device.device import DeviceFactory try: DeviceFactory.get_device(name) msg = DeviceFactory.differences_bewteen_devices_descriptions( name, requested_device_def) if msg: raise WrongUsage(msg) return False # Device exists and have the same construct parameters except KeyError: return True
def _validate_start(self, *args, **kwargs): # check base class invariants first if self.done(): raise WrongUsage("You can't run same {} multiple times. It is already done.".format(self)) if not self.connection: # only if we have connection we can expect some data on it # at the latest "just before start" we need connection raise NoConnectionProvided(self) # ---------------------------------------------------------------------- # We intentionally do not check if connection is open here. # In such case net result anyway will be failed/timeouted observer - # - so, user will need to investigate "why". # Checking connection state would benefit in early detection of: # "error condition - no chance to succeed since connection is closed". # However, drawback is a requirement on connection to have is_open() API # We choose minimalistic dependency over better troubleshooting support. # ---------------------------------------------------------------------- if self.timeout <= 0.0: raise ConnectionObserverTimeout(self, self.timeout, "before run", "timeout is not positive value")