# trigger.twister — Asynchronous device interaction library¶

Login and basic command-line interaction support using the Twisted asynchronous I/O framework. The Trigger Twister is just like the Mersenne Twister, except not at all.

class trigger.twister.IncrementalXMLTreeBuilder(callback, *args, **kwargs)

Version of XMLTreeBuilder that runs a callback on each tag.

We need this because JunoScript treats the entire session as one XML document. IETF NETCONF fixes that.

class trigger.twister.Interactor(log_to=None)

Creates an interactive shell.

Intended for use as an action with pty_connect(). See gong for an example.

connectionMade()

Fire up stdin/stdout once we connect.

dataReceived(data)

And write data to the terminal.

loseConnection()

Terminate the connection. Link this to the transport method of the same name.

class trigger.twister.IoslikeSendExpect(device, commands, incremental=None, with_errors=False, timeout=None, command_interval=0)

Action for use with TriggerTelnet as a state machine.

Take a list of commands, and send them to the device until we run out or one errors. Wait for a prompt after each.

connectionMade()

Do this when we connect.

dataReceived(bytes)

Do this when we get data.

timeoutConnection()

Do this when we timeout.

class trigger.twister.TriggerClientFactory(deferred, creds=None, init_commands=None)

Factory for all clients. Subclass me.

clientConnectionFailed(connector, reason)

Do this when the connection fails.

clientConnectionLost(connector, reason)

Do this when the connection is lost.

stopFactory()

This will be called before I stop listening on all Ports/Connectors.

This can be overridden to perform ‘shutdown’ tasks such as disconnecting database connections, closing files, etc.

It will be called, for example, before an application shuts down, if it was connected to a port. User code should not call this function directly.

class trigger.twister.TriggerSSHAsyncPtyChannel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

An SSH channel that requests a non-interactive pty intended for async usage.

Some devices won’t allow a shell without a pty, so we have to do a ‘pty-req’.

This is distinctly different from ~trigger.twister.TriggerSSHPtyChannel which is intended for interactive end-user sessions.

channelOpen(data)

Do this when the channel opens.

class trigger.twister.TriggerSSHChannelBase(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

Base class for SSH channels.

The method self._setup_channelOpen() should be called by channelOpen() in the subclasses. Before you subclass, however, see if you can’t just use TriggerSSHGenericChannel as-is!

channelOpen(data)

Do this when the channel opens.

dataReceived(bytes)

Do this when we receive data.

loseConnection()

Terminate the connection. Link this to the transport method of the same name.

timeoutConnection()

Do this when the connection times out.

class trigger.twister.TriggerSSHChannelFactory(deferred, commands, creds=None, incremental=None, with_errors=False, timeout=None, channel_class=None, command_interval=0, prompt_pattern=None, device=None, connection_class=None)

Intended to be used as a parent of automated SSH channels (e.g. Junoscript, NetScreen, NetScaler) to eliminate boiler plate in those subclasses.

buildProtocol(addr)

Create an instance of a subclass of Protocol.

The returned instance will handle input on an incoming server connection, and an attribute “factory” pointing to the creating factory.

Alternatively, L{None} may be returned to immediately close the new connection.

Override this method to alter how Protocol instances get created.

class trigger.twister.TriggerSSHCommandChannel(command, *args, **kwargs)

Run SSH commands on a system using ‘exec’

This will multiplex channels over a single connection. Because of the nature of the multiplexing setup, the master list of commands is stored on the SSH connection, and the state of each command is stored within each individual channel which feeds its result back to the factory.

channelOpen(data)

Do this when the channel opens.

closeReceived()

Called when the other side has closed the channel.

closed()

Called when the channel is closed. This means that both our side and the remote side have closed the channel.

dataReceived(bytes)

Do this when we receive data.

eofReceived()

Called when the other side will send no more data.

loseConnection()

Default loseConnection

send_next_command()

Send the next command in the stack stored on the connection

class trigger.twister.TriggerSSHConnection(commands=None, *args, **kwargs)

Used to manage, you know, an SSH connection.

Optionally takes a list of commands that may be passed on.

channelClosed(channel)

Forcefully close the transport connection when a channel closes connection. This is assuming only one channel is open.

serviceStarted()

Open the channel once we start.

class trigger.twister.TriggerSSHGenericChannel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

An SSH channel using all of the Trigger defaults to interact with network devices that implement SSH without any tricks.

Currently A10, Cisco, Brocade, NetScreen can simply use this. Nice!

Before you create your own subclass, see if you can’t use me as-is!

class trigger.twister.TriggerSSHJunoscriptChannel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

An SSH channel to execute Junoscript commands on a Juniper device running Junos.

This completely assumes that we are the only channel in the factory (a TriggerJunoscriptFactory) and walks all the way back up to the factory for its arguments.

channelOpen(data)

Do this when channel opens.

dataReceived(data)

Do this when we receive data.

class trigger.twister.TriggerSSHMultiplexConnection(commands=None, *args, **kwargs)

Used for multiplexing SSH ‘exec’ channels on a single connection.

Opens a new channel for each command in the stack once the previous channel has closed. In this pattern the Connection and the Channel are intertwined.

channelClosed(channel)

Close the channel when we’re done. But not the transport connection

send_command()

Send the next command in the stack once the previous channel has closed

class trigger.twister.TriggerSSHNetscalerChannel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

An SSH channel to interact with Citrix NetScaler hardware.

It’s almost a generic SSH channel except that we must check for errors first, because a prompt is not returned when an error is received. This had to be accounted for in the dataReceived() method.

dataReceived(bytes)

Do this when we receive data.

class trigger.twister.TriggerSSHPica8Channel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)
channelOpen(data)

Override channel open, which is where commanditer is setup in the base class.

class trigger.twister.TriggerSSHPtyChannel(localWindow=0, localMaxPacket=0, remoteWindow=0, remoteMaxPacket=0, conn=None, data=None, avatar=None)

Used by pty_connect() to turn up an interactive SSH pty channel.

channelOpen(data)

Setup the terminal when the channel opens.

class trigger.twister.TriggerSSHPtyClientFactory(deferred, action, creds=None, display_banner=None, init_commands=None, device=None)

Factory for an interactive SSH connection.

‘action’ is a Protocol that will be connected to the session after login. Use it to interact with the user and pass along commands.

class trigger.twister.TriggerSSHTransport

SSH transport with Trigger’s defaults.

Call with magic factory attributes creds, a tuple of login credentials, and connection_class, the class of channel to open, and commands, the list of commands to pass to the connection.

connectionLost(reason)

Detect when the transport connection is lost, such as when the remote end closes the connection prematurely (hosts.allow, etc.)

connectionMade()

Once the connection is up, set the ciphers but don’t do anything else!

connectionSecure()

Once we’re secure, authenticate.

dataReceived(data)

Explicity override version detection for edge cases where “SSH-” isn’t on the first line of incoming data.

receiveError(reason, desc)

Do this when we receive an error.

sendDisconnect(reason, desc)

Trigger disconnect of the transport.

verifyHostKey(pubKey, fingerprint)

Verify host key, but don’t actually verify. Awesome.

class trigger.twister.TriggerSSHUserAuth(user, options, *args)

Perform user authentication over SSH.

getGenericAnswers(name, information, prompts)

Send along the password when authentication mechanism is not ‘password’ This is most commonly the case with ‘keyboard-interactive’, which even when configured within self.preferredOrder, does not work using default getPassword() method.

getPassword(prompt=None)

ssh_USERAUTH_BANNER(packet)

Display SSH banner.

ssh_USERAUTH_FAILURE(packet)

An almost exact duplicate of SSHUserAuthClient.ssh_USERAUTH_FAILURE modified to forcefully disconnect. If we receive authentication failures, instead of looping until the server boots us and performing a sendDisconnect(), we raise a LoginFailure and call loseConnection().

See the base docstring for the method signature.

class trigger.twister.TriggerTelnet(timeout=60)

Telnet-based session login state machine. Primarily used by IOS-like type devices.

enableRemote(option)

Allow telnet clients to enable options if for some reason they aren’t enabled already (e.g. ECHO). (Ref: http://bit.ly/wkFZFg) For some reason Arista Networks hardware is the only vendor that needs this method right now.

login_state_machine(bytes)

state_enable()

Special Foundry breakage because they don’t do auto-enable from TACACS by default. Use ‘aaa authentication login privilege-mode’. Also, why no space after the Password: prompt here?

state_enable_pw()

Pass the enable password from the factory or NetDevices

state_logged_in()

Once we’re logged in, exit state machine and pass control to the action.

state_login_pw()

state_password()

After we got password prompt, check for enabled prompt.

state_percent_error()

Found a % error message. Don’t return immediately because we don’t have the error text yet.

state_raise_error()

Do this when we get a login failure.

state_username()

timeoutConnection()

Do this when we timeout logging in.

class trigger.twister.TriggerTelnetClientFactory(deferred, action, creds=None, loginpw=None, enablepw=None, init_commands=None, device=None)

Factory for a telnet connection.

trigger.twister.connect(device, init_commands=None, output_logger=None, login_errback=None, reconnect_handler=None)

Connect to a network device via pty for an interactive shell.

Parameters: device – A NetDevice object. init_commands – (Optional) A list of commands to execute upon logging into the device. If not set, they will be attempted to be read from .gorc. output_logger – (Optional) If set all data received by the device, including user input, will be written to this logger. This logger must behave like a file-like object and a implement a write() method. Hint: Use StringIO. login_errback – (Optional) An callable to be used as an errback that will handle the login failure behavior. If not set the default handler will be used. reconnect_handler – (Optional) A callable to handle the behavior of an authentication failure after a login has failed. If not set default handler will be used.
trigger.twister.execute(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0, force_cli=False)

Connect to a device and sequentially execute all the commands in the iterable commands.

Returns a Twisted Deferred object, whose callback will get a sequence of all the results after the connection is finished.

commands is usually just a list, however, you can have also make it a generator, and have it and incremental share a closure to some state variables. This allows you to determine what commands to execute dynamically based on the results of previous commands. This implementation is experimental and it might be a better idea to have the incremental callback determine what command to execute next; it could then be a method of an object that keeps state.

BEWARE: Your generator cannot block; you must immediately decide what next command to execute, if any.

Any None in the command sequence will result in a None being placed in the output sequence, with no command issued to the device.

If any command returns an error, the connection is dropped immediately and the errback will fire with the failed command. You may set with_errors to get the exception objects in the list instead.

Connection failures will still fire the errback.

LoginTimeout errors are always possible if the login process takes longer than expected and cannot be disabled.

Parameters: device – A NetDevice object commands – An iterable of commands to execute (without newlines). creds – (Optional) A 2-tuple of (username, password). If unset it will fetch it from .tacacsrc. incremental – (Optional) A callback that will be called with an empty sequence upon connection and then called every time a result comes back from the device, with the list of all results. with_errors – (Optional) Return exceptions as results instead of raising them timeout – (Optional) Command response timeout in seconds. Set to None to disable. The default is in settings.DEFAULT_TIMEOUT. CommandTimeout errors will result if a command seems to take longer to return than specified. command_interval – (Optional) Amount of time in seconds to wait between sending commands. force_cli – (Optional) Juniper-only: Force use of CLI instead of Junoscript. A Twisted Deferred object
trigger.twister.execute_async_pty_ssh(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0, prompt_pattern=None)

Execute via SSH for a device that requires shell + pty-req.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_exec_ssh(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Use multiplexed SSH ‘exec’ command channels to execute commands.

This will maintain a single SSH connection and run each new command in a separate channel after the previous command completes.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_generic_ssh(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0, channel_class=None, prompt_pattern=None, method='Generic', connection_class=None)

Use default SSH channel to execute commands on a device. Should work with anything not wonky.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_ioslike(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0, loginpw=None, enablepw=None)

Execute commands on a Cisco/IOS-like device. It will automatically try to connect using SSH if it is available and not disabled in settings.py. If SSH is unavailable, it will fallback to telnet unless that is also disabled in the settings. Otherwise it will fail, so you should probably make sure one or the other is enabled!

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_ioslike_ssh(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Execute via SSH for IOS-like devices with some exceptions.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_ioslike_telnet(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0, loginpw=None, enablepw=None)

Execute commands via telnet on a Cisco/IOS-like device.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_junoscript(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Connect to a Juniper device and enable Junoscript XML mode. All commands are expected to be XML commands (ElementTree.Element objects suitable for wrapping in <rpc> elements). Errors are expected to be of type xnm:error. Note that prompt detection is not used here.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_netscaler(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Execute commands on a NetScaler device.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_netscreen(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Execute commands on a NetScreen device running ScreenOS. For NetScreen devices running Junos, use execute_junoscript.

Please see execute for a full description of the arguments and how this works.

trigger.twister.execute_pica8(device, commands, creds=None, incremental=None, with_errors=False, timeout=300, command_interval=0)

Execute commands on a Pica8 device. This is only needed to append ‘| no-more’ to show commands because Pica8 currently (v2.2) lacks a global command to disable paging.

Please see execute for a full description of the arguments and how this works.

trigger.twister.handle_login_failure(failure)

An errback to try detect a login failure

Parameters: failure – A Twisted Failure instance
trigger.twister.has_ioslike_error(s)

Test whether a string seems to contain an IOS-like error.

trigger.twister.has_juniper_error(s)

Test whether a string seems to contain an Juniper error.

trigger.twister.has_junoscript_error(tag)

Test whether an Element contains a Junoscript xnm:error.

trigger.twister.has_netscaler_error(s)

Test whether a string seems to contain a NetScaler error.

trigger.twister.is_awaiting_confirmation(prompt)

Checks if a prompt is asking for us for confirmation and returns a Boolean.

New patterns may be added by customizing settings.CONTINUE_PROMPTS.

>>> from trigger.twister import is_awaiting_confirmation
>>> is_awaiting_confirmation('Destination filename [running-config]? ')
True

Parameters: prompt – The prompt string to check
trigger.twister.pty_connect(device, action, creds=None, display_banner=None, ping_test=False, init_commands=None)

Connect to a device and log in. Use SSHv2 or telnet as appropriate.

Parameters: device – A NetDevice object. action – A Twisted Protocol instance (not class) that will be activated when the session is ready. creds – A 2-tuple (username, password). By default, credentials from .tacacsrc will be used according to settings.DEFAULT_REALM. Override that here. display_banner – Will be called for SSH pre-authentication banners. It will receive two args, banner and language. By default, nothing will be done with the banner. ping_test – If set, the device is pinged and must succeed in order to proceed. init_commands – A list of commands to execute upon logging into the device. A Twisted Deferred object
trigger.twister.requires_enable(proto_obj, data)

Check if a device requires enable.

Parameters: proto_obj – A Protocol object such as an SSHChannel data – The channel data to check for an enable prompt
trigger.twister.send_enable(proto_obj, disconnect_on_fail=True)

Send ‘enable’ and enable password to device.

Parameters: proto_obj – A Protocol object such as an SSHChannel disconnect_on_fail – If set, will forcefully disconnect on enable password failure
trigger.twister.stop_reactor`()

Stop the reactor if it’s already running.