def test_get_connection_without_variant_selection_raises_KeyError(): from moler.connection 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)
def test_get_connection_must_use_either_name_or_io_type(): from moler.connection 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)
def test_get_connection_may_not_use_both__name_and_io_type(): from moler.connection 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)
def test_cannot_select_connection_by_nonexisting_name(connections_config): """Non-existing means here not defined inside configuration""" from moler.connection 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)
def test_cannot_select_nonexisting_connection_variant(connections_config): """Non-existing means not registered inside ConnectionFactory""" from moler.connection 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)
def test_factory_has_buildin_constructors_active_by_default(): from moler.connection 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 main(connections2observe4ip): # Starting the servers servers = [] for address, _, ping_ip in connections2observe4ip: # simulate pinging given IP server_thread, server_done = start_ping_sim_server(address, ping_ip) servers.append((server_thread, server_done)) # 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() # stop servers for server_thread, server_done in servers: server_done.set() server_thread.join()
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 main(connections2observe4ip): # Starting the servers servers = [] for address, ping_ip in connections2observe4ip: # simulate pinging given IP server_thread, server_done = start_ping_sim_server(address, ping_ip) servers.append((server_thread, server_done)) # 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() # stop servers for server_thread, server_done in servers: server_done.set() server_thread.join()
def test_can_select_connection_variant_from_buildin_connections(connections_config): from moler.connection 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_returned_connections_have_moler_integrated_connection( builtin_variant, builtin_io_type_example): from moler.connection 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_config_file(moler_config): from moler.connection 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_can_select_connection_by_name(connections_config): from moler.connection 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_loaded_from_env_variable(moler_config, monkeypatch): from moler.connection 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(from_env_var="MOLER_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 __init__(self, io_connection=None, io_type=None, variant=None, sm_params=dict()): """ 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 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) """ self.logger = logging.getLogger('moler.textualdevice') self.states = [] self.goto_states_triggers = [] # Below line will modify self extending it with methods and atributes 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 = {} self._state_prompts = {} self._prompts_events = {} self._configurations = dict() self._prepare_transitions() self._prepare_state_hops() self._configure_state_machine(sm_params) if io_connection: self.io_connection = io_connection else: self.io_connection = get_connection(io_type=io_type, variant=variant) self.io_connection.notify(callback=self.on_connection_made, when="connection_made") # TODO: Need test to ensure above sentence for all connection self.io_connection.open() 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._collect_cmds_for_state_machine() self._collect_events_for_state_machine() self._run_prompts_observers() self._default_prompt = re.compile(r'^[^<]*[\$|%|#|>|~]\s*$')
def test_can_select_connection_variant_from_plugin_connections(builtin_connection_factories, connections_config): from moler.connection 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_select_connection_loaded_from_dict(moler_config): from moler.connection 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 test_can_plugin_alternative_connection_instead_of_builtin_one( builtin_connection_factories): from moler.connection import ConnectionFactory, get_connection from moler.connection import ObservableConnection class DummyTcpConnection(object): def __init__(self, host, port): self.moler_connection = ObservableConnection(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) tcp_connection.moler_connection.name = "{}:{}".format(host, 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): 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 from_named_connection(cls, connection_name): io_conn = get_connection(name=connection_name) return cls(io_connection=io_conn)
from moler.cmd.unix.ps import Ps from moler.connection import ObservableConnection, get_connection from moler.io.raw.terminal import ThreadedTerminal # v.1 - combine all manually # moler_conn = ObservableConnection() # terminal = ThreadedTerminal(moler_connection=moler_conn) # v.2 - let factory combine # terminal = get_connection(io_type='terminal', variant='threaded') # v.3 - let factory select default variant terminal = get_connection(io_type='terminal') terminal.open() ps_cmd = Ps(connection=terminal.moler_connection, options="-ef") processes = ps_cmd() for proc in processes: if 'python' in proc['CMD']: print("PID: {} CMD: {}".format(proc['PID'], proc['CMD'])) terminal.close() # result: """ PID: 1817 CMD: /usr/bin/python /usr/share/system-config-printer/applet.py PID: 21825 CMD: /usr/bin/python /home/gl/moler/examples/command/unix_ps.py """
def test_can_get_connection(): from moler.connection import get_connection tcp_connection = get_connection(io_type='tcp', variant='asyncio-in-thread', host='localhost', port=2345) assert tcp_connection is not None
def __init__(self, sm_params=None, name=None, io_connection=None, io_type=None, variant=None, io_constructor_kwargs={}, initial_state=None): """ 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. """ 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 # Below line will modify self extending it with methods and atributes 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 = {} self._state_prompts = {} self._prompts_events = {} 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.io_connection.open() self._cmdnames_available_in_state = dict() self._eventnames_available_in_state = dict() self._collect_cmds_for_state_machine() self._collect_events_for_state_machine() self._run_prompts_observers() self._default_prompt = re.compile(r'^[^<]*[\$|%|#|>|~]\s*$')
from moler.cmd.unix.ps import Ps from moler.connection import ObservableConnection, get_connection from moler.io.raw.terminal import ThreadedTerminal # v.1 - combine all manually # moler_conn = ObservableConnection() # terminal = ThreadedTerminal(moler_connection=moler_conn) # v.2 - let factory combine terminal = get_connection(io_type='terminal', variant='threaded') # v.3 - let factory select default variant # terminal = get_connection(io_type='terminal') with terminal.open(): ps_cmd = Ps(connection=terminal.moler_connection, options="-ef") processes = ps_cmd() for proc in processes: if 'python' in proc['CMD']: print("PID: {} CMD: {}".format(proc['PID'], proc['CMD'])) # result: """ PID: 1817 CMD: /usr/bin/python /usr/share/system-config-printer/applet.py PID: 21825 CMD: /usr/bin/python /home/gl/moler/examples/command/unix_ps.py """