def test_get_connection_without_variant_selection_raises_KeyError(): from moler.connection_factory import get_connection with pytest.raises(KeyError) as err: get_connection(io_type='tcp', host='localhost', port=2345) assert "No variant selected (directly or via configuration) for 'tcp' connection" in str( err.value)
def test_get_connection_may_not_use_both__name_and_io_type(): from moler.connection_factory import get_connection with pytest.raises(AssertionError) as err: get_connection(name='www_server_1', io_type='tcp', host='localhost', port=2345) assert "Use either 'name' or 'io_type' parameter (not both)" in str(err.value)
def test_get_connection_must_use_either_name_or_io_type(): from moler.connection_factory import get_connection with pytest.raises(AssertionError) as err: get_connection(host='localhost', port=2345) assert "Provide either 'name' or 'io_type' parameter (none given)" in str( err.value)
def test_cannot_select_nonexisting_connection_variant(connections_config): """Non-existing means not registered inside ConnectionFactory""" from moler.connection_factory import get_connection connections_config.set_default_variant(io_type='tcp', variant='yedi_magic') with pytest.raises(KeyError) as err: get_connection(io_type='tcp', host='localhost', port=2345) assert "'yedi_magic' variant of 'tcp' connection is not registered inside ConnectionFactory" in str(err.value)
def test_cannot_select_connection_by_nonexisting_name(connections_config): """Non-existing means here not defined inside configuration""" from moler.connection_factory import get_connection connections_config.set_default_variant(io_type='tcp', variant='threaded') with pytest.raises(KeyError) as err: get_connection(name='www_server_1') assert "Connection named 'www_server_1' was not defined inside configuration" in str(err.value)
def test_connection_factory_can_build_sshshell_based_on_other_sshshell_for_sshtransport_reuse(): from moler.connection_factory import get_connection conn1 = get_connection(io_type='sshshell', host='localhost', port=2222, username="******", password="******") conn2 = get_connection(io_type='sshshell', reuse_ssh_of_shell=conn1) assert conn2.sshshell.host == "localhost" assert conn2.sshshell.port == 2222 assert conn2.sshshell.username == "moler" assert conn2.sshshell.password == "moler_passwd"
def test_factory_has_buildin_constructors_active_by_default(): from moler.connection_factory import get_connection conn = get_connection(io_type='memory', variant='threaded') assert conn.__module__ == 'moler.io.raw.memory' assert conn.__class__.__name__ == 'ThreadedFifoBuffer' conn = get_connection(io_type='tcp', variant='threaded', host='localhost', port=2345) assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp'
def test_correct_call_of_sshshell_construction_based_on_existing_sshshell_connection(): from moler.connection_factory import get_connection from moler.exceptions import MolerException existing_conn = get_connection(io_type='sshshell', variant='threaded', host='localhost', port=2345, login='******', password='******') with pytest.raises(MolerException) as err: get_connection(io_type='sshshell', variant='threaded', reuse_ssh_of_shell=existing_conn, port=2345, login='******', password='******') assert "Don't use host/port/username/login/password when building sshshell reusing ssh of other sshshell" in str(err.value) conn_reusing_ssh_transport = get_connection(io_type='sshshell', variant='threaded', reuse_ssh_of_shell=existing_conn) assert conn_reusing_ssh_transport.__class__.__name__ == 'ThreadedSshShell'
def test_can_select_connection_variant_from_buildin_connections(connections_config): from moler.connection_factory import get_connection connections_config.set_default_variant(io_type='tcp', variant='threaded') conn = get_connection(io_type='tcp', host='localhost', port=2345) assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp'
def test_can_get_connection(): from moler.connection_factory import get_connection tcp_connection = get_connection(io_type='tcp', variant='asyncio-in-thread', host='localhost', port=2345) assert tcp_connection is not None
async def main(connections2observe4ip): logger = logging.getLogger('asyncio.main') logger.debug('starting jobs observing connections') # Starting the clients jobs_on_connections = [] for _, connection_name, ping_ip in connections2observe4ip: # ------------------------------------------------------------------ # This front-end code hides all details of connection. # We just use its name - such name should be meaningful for user. # like: "main_dns_server", "backup_ntp_server", ... # Another words, all we want here is stg like: # "give me connection to main_dns_server" # ------------------------------------------------------------------ con_logger = logging.getLogger( 'tcp-async-io.{}'.format(connection_name)) tcp_connection = get_connection(name=connection_name, variant='asyncio', logger=con_logger) # client_task= asyncio.ensure_future(ping_observing_task(tcp_connection, ping_ip)) jobs_on_connections.append(ping_observing_task(tcp_connection, ping_ip)) # await observers job to be done completed, pending = await asyncio.wait(jobs_on_connections) logger.debug('all jobs observing connections are done')
def test_connection_factory_can_use_alternate_login_param_of_sshshell(): from moler.connection_factory import get_connection conn = get_connection(io_type='sshshell', host='localhost', port=2222, login="******", password="******") assert conn.__module__ == 'moler.io.raw.sshshell' assert conn.__class__.__name__ == 'ThreadedSshShell'
def main(connections2observe4ip): logger = logging.getLogger('asyncio.main') logger.debug('starting jobs observing connections') # Starting the clients jobs_on_connections = [] for connection_name, ping_ip in connections2observe4ip: # ------------------------------------------------------------------ # This front-end code hides all details of connection. # We just use its name - such name should be meaningful for user. # like: "main_dns_server", "backup_ntp_server", ... # Another words, all we want here is stg like: # "give me connection to main_dns_server" # ------------------------------------------------------------------ # con_logger = logging.getLogger('tcp-async_in_thrd-io.{}'.format(connection_name)) # tcp_connection = get_connection(name=connection_name, variant='asyncio-in-thread', logger=con_logger) tcp_connection = get_connection(name=connection_name, variant='asyncio-in-thread') client_thread = threading.Thread(target=ping_observing_task, args=(tcp_connection, ping_ip)) client_thread.start() jobs_on_connections.append(client_thread) # await observers job to be done for client_thread in jobs_on_connections: client_thread.join() logger.debug('all jobs observing connections are done')
def test_connection_factory_has_threaded_registered_as_default_variant_of_sshshell(): from moler.connection_factory import get_connection conn = get_connection(io_type='sshshell', host='localhost', port=2222, username="******", password="******") assert conn.__module__ == 'moler.io.raw.sshshell' assert conn.__class__.__name__ == 'ThreadedSshShell'
def test_returned_connections_have_moler_integrated_connection(builtin_variant, builtin_io_type_example): from moler.connection_factory import get_connection io_type, kwargs = builtin_io_type_example conn = get_connection(io_type=io_type, variant=builtin_variant, **kwargs) assert hasattr(conn, 'moler_connection') assert conn.moler_connection.how2send != conn.moler_connection._unknown_send
def test_can_select_connection_loaded_from_dict_as_positional_args(moler_config, args): from moler.connection_factory import get_connection moler_config.load_config(*args) conn = get_connection(name='www_server_1') assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp' assert conn.host == 'localhost' assert conn.port == 2344
def test_can_select_connection_loaded_from_config_file(moler_config): from moler.connection_factory import get_connection conn_config = os.path.join(os.path.dirname(__file__), "resources", "www_servers_connections.yml") moler_config.load_config(config=conn_config, config_type='yaml') conn = get_connection(name='www_server_1') assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp' assert conn.host == 'localhost' assert conn.port == 2345
def test_connection_factory_has_sshshell_constructor_active_by_default(): from moler.connection_factory import get_connection conn = get_connection(io_type='sshshell', variant='threaded', host='localhost', port=2222, username="******", password="******") assert conn.__module__ == 'moler.io.raw.sshshell' assert conn.__class__.__name__ == 'ThreadedSshShell' assert hasattr(conn, 'moler_connection') assert conn.sshshell.host == "localhost" assert conn.sshshell.port == 2222 assert conn.sshshell.username == "moler" assert conn.sshshell.password == "moler_passwd"
def test_can_select_connection_loaded_from_env_variable(moler_config, monkeypatch, params): from moler.connection_factory import get_connection conn_config = os.path.join(os.path.dirname(__file__), "resources", "www_servers_connections.yml") monkeypatch.setitem(os.environ, 'MOLER_CONFIG', conn_config) moler_config.load_config(**params) conn = get_connection(name='www_server_1') assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp' assert conn.host == 'localhost' assert conn.port == 2345
def test_can_select_connection_by_name(connections_config): from moler.connection_factory import get_connection connections_config.define_connection(name="www_server_1", io_type='tcp', host='localhost', port=2345) connections_config.set_default_variant(io_type='tcp', variant='threaded') conn = get_connection(name='www_server_1') assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp' assert conn.host == 'localhost' assert conn.port == 2345
def test_can_select_connection_variant_from_plugin_connections(builtin_connection_factories, connections_config): from moler.connection_factory import ConnectionFactory, get_connection class DummyTcpConnection(object): def __init__(self, host, port): pass ConnectionFactory.register_construction(io_type='tcp', variant='dummy', constructor=DummyTcpConnection) connections_config.set_default_variant(io_type='tcp', variant='dummy') conn = get_connection(io_type='tcp', host='localhost', port=2345) assert conn.__class__.__name__ == 'DummyTcpConnection'
def test_can_plugin_alternative_connection_instead_of_builtin_one(builtin_connection_factories): from moler.connection_factory import ConnectionFactory, get_connection from moler.threaded_moler_connection import ThreadedMolerConnection class DummyTcpConnection(object): def __init__(self, host, port): self.moler_connection = ThreadedMolerConnection(how2send=self.send) def send(self, data): pass ConnectionFactory.register_construction(io_type='tcp', variant='threaded', constructor=DummyTcpConnection) conn = get_connection(io_type='tcp', variant='threaded', host='localhost', port=2345) assert conn.__class__.__name__ == 'DummyTcpConnection'
def main(connections2observe4ip): # Starting the clients connections = [] for address, ping_ip in connections2observe4ip: host, port = address # ------------------------------------------------------------------ # This front-end code hides parallelism variant # used to read data from connection. # We don't care if it is TCP connection based on threads or asyncio. # All we want here is "any TCP connection towards given host/port". # "any" means here: TCP variant as configured on backend. # ------------------------------------------------------------------ tcp_connection = get_connection(io_type='tcp', host=host, port=port) client_thread = threading.Thread(target=ping_observing_task, args=(tcp_connection, ping_ip)) client_thread.start() connections.append(client_thread) # await observers job to be done for client_thread in connections: client_thread.join()
def main(connections2observe4ip): # Starting the clients connections = [] for _, connection_name, ping_ip in connections2observe4ip: # ------------------------------------------------------------------ # This front-end code hides all details of connection. # We just use its name - such name should be meaningful for user. # like: "main_dns_server", "backup_ntp_server", ... # Another words, all we want here is stg like: # "give me connection to main_dns_server" # ------------------------------------------------------------------ tcp_connection = get_connection(name=connection_name) tcp_connection.moler_connection.name = connection_name client_thread = threading.Thread(target=ping_observing_task, args=(tcp_connection, ping_ip)) client_thread.start() connections.append(client_thread) # await observers job to be done for client_thread in connections: client_thread.join()
def test_can_select_connection_loaded_from_dict(moler_config): from moler.connection_factory import get_connection configuration_in_dict = { 'NAMED_CONNECTIONS': { 'www_server_1': { 'io_type': 'tcp', 'host': 'localhost', 'port': 2344 } }, 'IO_TYPES': { 'default_variant': { 'tcp': 'threaded' } } } moler_config.load_config(config=configuration_in_dict, config_type='dict') conn = get_connection(name='www_server_1') assert conn.__module__ == 'moler.io.raw.tcp' assert conn.__class__.__name__ == 'ThreadedTcp' assert conn.host == 'localhost' assert conn.port == 2344
def __init__(self, sm_params=None, name=None, io_connection=None, io_type=None, variant=None, io_constructor_kwargs=None, initial_state=None, lazy_cmds_events=False): """ Create Device communicating over io_connection CAUTION: Device owns (takes over ownership) of connection. It will be open when device "is born" and close when device "dies". :param sm_params: dict with parameters of state machine for device :param name: name of device :param io_connection: External-IO connection having embedded moler-connection :param io_type: type of connection - tcp, udp, ssh, telnet, ... :param variant: connection implementation variant, ex. 'threaded', 'twisted', 'asyncio', ... (if not given then default one is taken) :param io_constructor_kwargs: additional parameter into constructor of selected connection type (if not given then default one is taken) :param initial_state: name of initial state. State machine tries to enter this state just after creation. :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. """ super(TextualDevice, self).__init__() if io_constructor_kwargs is None: io_constructor_kwargs = dict() sm_params = copy_dict(sm_params, deep_copy=True) io_constructor_kwargs = copy_dict(io_constructor_kwargs, deep_copy=True) self.initial_state = initial_state if initial_state is not None else "NOT_CONNECTED" self.states = [TextualDevice.not_connected] self.goto_states_triggers = [] self._name = name self.device_data_logger = None self.timeout_keep_state = 10 # Timeout for background goto state after unexpected state change. self.lazy_cmds_events = lazy_cmds_events # Set True to lazy load commands and events. # Below line will modify self extending it with methods and attributes od StateMachine # For eg. it will add attribute self.state self.SM = StateMachine(model=self, states=self.states, initial=TextualDevice.not_connected, auto_transitions=False, queued=True) self._state_hops = dict() self._state_prompts = dict() self._state_prompts_lock = threading.Lock() self._reverse_state_prompts_dict = dict() self._prompts_event = None self._kept_state = None self._configurations = dict() self._newline_chars = dict() # key is state, value is chars to send as newline if io_connection: self.io_connection = io_connection else: self.io_connection = get_connection(io_type=io_type, variant=variant, **io_constructor_kwargs) self.io_connection.name = self.name self.io_connection.moler_connection.name = self.name self.logger = logging.getLogger('moler.connection.{}'.format(self.name)) self.configure_logger(name=self.name, propagate=False) self._prepare_transitions() self._prepare_state_hops() self._configure_state_machine(sm_params) self._prepare_newline_chars() # TODO: Need test to ensure above sentence for all connection self.io_connection.notify(callback=self.on_connection_made, when="connection_made") self.io_connection.notify(callback=self.on_connection_lost, when="connection_lost") self._cmdnames_available_in_state = dict() self._eventnames_available_in_state = dict() self._default_prompt = re.compile(r'^[^<]*[\$|%|#|>|~]\s*$') self._neighbour_devices = None self._established = False msg = "Created device '{}' as instance of class '{}.{}'.".format( self.name, self.__class__.__module__, self.__class__.__name__, ) self._log(level=logging.DEBUG, msg=msg) self._public_name = None self._warning_was_sent = False self._goto_state_lock = threading.Lock() self._goto_state_thread_manipulation_lock = threading.Lock() self._queue_states = queue.Queue() self._thread_for_goto_state = None self.SM.state_change_log_callable = self._log self.SM.current_state_callable = self._get_current_state
def from_named_connection(cls, connection_name): io_conn = get_connection(name=connection_name) return cls(io_connection=io_conn)
import time from moler.cmd.unix.ping import Ping from moler.connection_factory import get_connection host = 'www.google.com' terminal = get_connection(io_type='terminal', variant='threaded') with terminal.open(): ping_cmd = Ping(connection=terminal.moler_connection, destination=host, options="-w 6") print("Start pinging {} ...".format(host)) ping_cmd.start() print("Doing other stuff while pinging {} ...".format(host)) time.sleep(3) ping_stats = ping_cmd.await_done(timeout=4) print("ping {}: {}={}, {}={} [{}]".format(host, 'packet_loss', ping_stats['packet_loss'], 'time_avg', ping_stats['time_avg'], ping_stats['time_unit'])) # result: """ Start pinging www.google.com ... Doing other stuff while pinging www.google.com ... ping www.google.com: packet_loss=0, time_avg=50.000 [ms] """
def test_user_is_informed_about_terminal_io_unavailable_on_windows(): from moler.connection_factory import get_connection with mock.patch("moler.connection_factory.platform.system", return_value='Windows'): with pytest.raises(AttributeError) as err: get_connection(io_type='terminal', variant='threaded') assert "No 'terminal' connection available on Windows (try using 'sshshell' connection instead)" in str(err.value)