Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nokia/moler/llms.txt

Use this file to discover all available pages before exploring further.

A Command in Moler is a subclass of ConnectionObserver that actively sends a command string over a connection and parses the resulting output. Unlike a plain ConnectionObserver (which passively watches), a Command is the active counterpart: it triggers output on the device by writing to the connection, then collects and parses whatever comes back. Each Command encapsulates three concerns in a single object:
  • Triggering — sends the command string via connection.sendline(self.command_string)
  • Parsing — implements data_received() to extract structured data from the device output
  • Storing the result — calls set_result() when parsing is complete

The Future/Promise pattern

Commands implement the Futures and promises pattern. Running a command and parsing its output takes time, so the result is not available immediately after calling the command. Moler exposes this explicitly through two execution modes.
Call the command object directly like a function. It runs in the foreground and blocks until the command finishes, then returns the result.
from moler.config import load_config
from moler.device import DeviceFactory

load_config(config='my_devices.yml')
my_unix = DeviceFactory.get_device(name='MyMachine')
ps_cmd = my_unix.get_cmd(cmd_name="ps", cmd_params={"options": "-ef"})

# Call it like a function — blocks until done
processes_info = ps_cmd()
for proc_info in processes_info:
    if 'python' in proc_info['CMD']:
        print("PID: {info[PID]} CMD: {info[CMD]}".format(info=proc_info))
This is the simplest usage: create, call, process the result.

Command lifecycle

1

Create

Instantiate a command, passing a connection and any command-specific parameters.
# Via device (recommended)
ps_cmd = my_unix.get_cmd(cmd_name="ps", cmd_params={"options": "-ef"})

# Or directly
from moler.cmd.unix.ping import Ping
ping_cmd = Ping(connection=terminal.moler_connection,
                destination='www.google.com', options="-w 6")
2

Start (or call)

Either call the command directly (blocking) or call start() to run it in the background. On start, Moler sends command_string over the connection.
ping_cmd.start()   # non-blocking, returns self
# or
result = ps_cmd()  # blocking
3

Data received and parsed

As data arrives from the connection, data_received(data, recv_time) is called. The command parses each chunk and accumulates the result internally.
4

await_done / result

For background commands, call await_done(timeout) to block until the command signals completion. It returns the parsed result.
ping_stats = ping_cmd.await_done(timeout=6)

Results are dicts (or lists of dicts)

Commands return structured Python objects — almost always dict or list[dict] — making results easy to process in test code.
# ps returns a list of dicts
processes_info = ps_cmd()
for proc_info in processes_info:
    print("PID: {info[PID]} CMD: {info[CMD]}".format(info=proc_info))

# ping returns a single dict
ping_stats = ping_cmd.await_done(timeout=6)
print("packet_loss={}, time_avg={} [{}]".format(
    ping_stats['packet_loss'],
    ping_stats['time_avg'],
    ping_stats['time_unit']))

# ls returns a dict with a 'files' key
remote_files = ls_cmd()
if 'readme.txt' in remote_files['files']:
    info = remote_files['files']['readme.txt']
    print("size:", info['size_bytes'])

Running commands in parallel

Because commands implement the Future pattern, multiple commands can run concurrently. Start them all in the background, then await each result:
from moler.device import DeviceFactory

my_unix = DeviceFactory.get_device(name='MyMachine')
remote_unix = DeviceFactory.get_device(name='RebexTestMachine')
remote_unix.goto_state(state="UNIX_REMOTE")

ping_cmd = my_unix.get_cmd(cmd_name="ping",
                            cmd_params={"destination": "www.google.com", "options": "-w 6"})
ls_cmd = remote_unix.get_cmd(cmd_name="ls", cmd_params={"options": "-l"})

ping_cmd.start()                           # run ping in background
remote_files = ls_cmd()                    # run ls in foreground
ping_stats = ping_cmd.await_done(timeout=6)  # collect ping result
A command object cannot be reused after it is done. Create a new instance for each invocation.

Command vs. ConnectionObserver

ConnectionObserverCommand
Sends data?No (passive)Yes — writes command_string to connection
Triggers device output?NoYes
Typical useWatching for alarms, rebootsRunning ping, ls, ssh, etc.
is_command()FalseTrue
A Command is simply the “active” version of a ConnectionObserver. It inherits the same lifecycle (start, await_done, cancel, result) but additionally sends a string that causes the device to produce the output it will parse.

Events

Passive observers that watch for patterns and fire callbacks

Devices

State machines that manufacture commands for the current state