plesty.server.services.nodes

Reaching the nodes a master drives.

The console has always managed the bench it runs on. A master (plesty.server.model.agent) also shows the devices of the benches it declares as peers, and acts on them — so it needs, for the first time on this side, a client of another bench. That client already exists and is already what plesty-bench uses: plesty.bench.BenchClient. This module is the thin layer between it and the presenter.

One link per node, kept open. A poll every three seconds is not worth a connection each; the link reconnects on its own when a request times out.

A link is called from a thread pool, so every request holds the link’s lock: a ZMQ socket belongs to one thread at a time, and the window’s workers make no promise about which of them asks.

A node that does not answer is a state, not a failure. It keeps its row, its devices go unreachable, and NodeLink.trouble says why — a console that dropped the rows of a bench somebody rebooted would be reporting that the devices are gone.

Attributes

POLL_TIMEOUT_MS

ACTION_TIMEOUT_MS

Connect

Exceptions

NodeError

A node could not be reached, or refused what it was asked.

Classes

NodeLink

This master's link to one node's agent.

NodeDevice

One device of a node, on a connection this handle owns.

Nodes

Every node a master drives, one NodeLink each.

Functions

_connect(→ plesty.bench.BenchClient)

Module Contents

plesty.server.services.nodes.POLL_TIMEOUT_MS = 3000
plesty.server.services.nodes.ACTION_TIMEOUT_MS = 120000
plesty.server.services.nodes.Connect
exception plesty.server.services.nodes.NodeError

Bases: RuntimeError

A node could not be reached, or refused what it was asked.

Initialize self. See help(type(self)) for accurate signature.

plesty.server.services.nodes._connect(peer: plesty.server.model.Peer) → plesty.bench.BenchClient
Parameters:

peer (plesty.server.model.Peer)

Return type:

plesty.bench.BenchClient

This master’s link to one node’s agent.

Bind to peer; the socket opens on the first request.

Parameters:
peer
_connect
_client: plesty.bench.BenchClient | None = None
_lock
_trouble: str = ''
property name: str

The node’s name — what the Host column shows.

Return type:

str

property trouble: str

Why the last request failed, or empty after one that worked.

Return type:

str

property reachable: bool

Whether the last request got an answer.

Return type:

bool

close() → None

Drop the connection; a later request opens a new one.

Return type:

None

run(argv: collections.abc.Sequence[str], timeout_ms: int = ACTION_TIMEOUT_MS) → str

Run plesty-server <argv> on the node and return its output.

Parameters:
  • argv (collections.abc.Sequence[str]) – The command line, as the local CLI would take it.

  • timeout_ms (int) – Reply timeout.

Returns:

Standard output, stripped.

Raises:

NodeError – When the node did not answer, or the command failed — carrying what the node said, since it is the node that knows.

Return type:

str

devices() → list[dict[str, Any]]

Every device the node declares, as list --json reports them.

The same document the local list --json prints, so one call carries the declaration, the installed version, the source line and the state — everything a row needs, in one round trip per node per refresh.

Raises:

NodeError – When the node did not answer or gave no JSON.

Return type:

list[dict[str, Any]]

device(name: str) → NodeDevice

A handle on one running device of the node, for a control panel.

On a connection of its own, not the one this link polls with. A panel drives its handle from its own worker thread while the device table keeps polling on ours, and a ZMQ socket belongs to one thread at a time: sharing one would let a panel’s reply answer a poll, and every request after that would return the one before it.

Parameters:

name (str)

Return type:

NodeDevice

class plesty.server.services.nodes.NodeDevice(peer: plesty.server.model.Peer, name: str, connect: Connect)

One device of a node, on a connection this handle owns.

What a control panel drives. It is the four calls of plesty-lib’s DeviceHandle — describe, query, write, call — delegated to a plesty.bench.RemoteDevice, plus the one thing that handle cannot offer: a close that actually releases something.

plesty.bench.RemoteDevice closes nothing by design, because its connection belongs to the BenchClient that made it. A panel needs the opposite — a connection of its own, released when the panel goes — so that the console’s poll and the panel never share a socket, and so that the console’s _release has something to close.

The client connects lazily, so the socket is made by whichever thread makes the first call. That is the panel’s worker, which is the thread that must own it.

Open a handle on name at peer.

Parameters:
  • peer (plesty.server.model.Peer) – The node the device runs on.

  • name (str) – The declared device.

  • connect (Connect) – Builds the client; injected by the tests.

name
peer
_client
_device: Any
describe() → dict[str, Any]

The device’s methods and its serialized doc model.

Return type:

dict[str, Any]

query(key: str) → Any

Read configuration parameter key.

Parameters:

key (str)

Return type:

Any

write(key: str, value: Any) → Any

Write value to configuration parameter key.

Parameters:
  • key (str)

  • value (Any)

Return type:

Any

call(op: str, **kwargs: Any) → Any

Run operation op.

Parameters:
  • op (str)

  • kwargs (Any)

Return type:

Any

close() → None

Release this handle’s own connection.

Return type:

None

class plesty.server.services.nodes.Nodes(peers: collections.abc.Iterable[plesty.server.model.Peer] = (), connect: Connect | None = None)

Every node a master drives, one NodeLink each.

Hold a link per peer.

Parameters:
  • peers (collections.abc.Iterable[plesty.server.model.Peer]) – The declared nodes.

  • connect (Connect | None) – Builds each client; injected by the tests.

_connect = None
refresh(peers: collections.abc.Iterable[plesty.server.model.Peer]) → None

Make the links match peers, keeping the connections still wanted.

A peer whose address or secret changed is reconnected; one that is simply still there keeps the socket it has, so editing the list does not cost every other node its connection.

Parameters:

peers (collections.abc.Iterable[plesty.server.model.Peer])

Return type:

None

The links, in declaration order.

Return type:

list[NodeLink]

property names: list[str]

The node names.

Return type:

list[str]

The link to the node name.

Raises:

NodeError – When no such node is declared.

Parameters:

name (str)

Return type:

NodeLink

close() → None

Drop every connection.

Return type:

None