Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/netlab/exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

**netlab exec** command uses information stored in the _netlab_ snapshot file and reported with the **[`netlab inspect --node`](inspect.md)** command to execute a command on one or more lab devices using SSH or **docker exec**.

[**netlab inspect** documentation](netlab-inspect-node) describes how to specify the nodes on which the command will be executed.

## Usage

```text
Expand All @@ -20,6 +22,7 @@ options:
-q, --quiet Report only major errors
--dry-run Print the hosts and the commands that would be executed on them,
but do not execute them
--header Add node headers before command printouts
-i INSTANCE, --instance INSTANCE
Specify lab instance to execute commands in

Expand Down
4 changes: 3 additions & 1 deletion docs/netlab/inspect.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,17 @@ Finally, the data selection argument is evaluated as a Python expression, so you
(netlab-inspect-node)=
## Node Inspection Examples

You can use the `--node` parameter to inspect the data structure of a single node, a list of nodes (separated by commas), or a wildcard expression.
You can use the `--node` parameter to inspect the data structure of a single node, a group of nodes, or a wildcard (glob) expression. You can also specify multiple parameters separated by commas.

| To display this information... | ...use this command |
|--------------------------------|---------------------|
| data for node `r1` | `netlab inspect --node r1` |
| interface data for node `r1` | `netlab inspect --node r1 interfaces` |
| first interface on node `r1` | `netlab inspect --node r1 'interfaces[0]'` |
| BGP parameters on R1 and R2 | `netlab inspect --node r1,r2 bgp` |
| BGP parameters of routers in group as65101 | `netlab inspect --node as65101 bgp` |
| VRFs on all nodes | `netlab inspect --node '*' vrfs` |
| VLANs on all nodes start with 'r' | `netlab inspect --node 'r*' vlans` |

```{warning}
You can use Python expressions only when specifying a single node to inspect.
Expand Down
4 changes: 4 additions & 0 deletions docs/netlab/report.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ The **[netlab show reports](netlab-show-reports)** command displays up-to-date l
* `netlab report bgp-neighbor.md` creates the table of BGP neighbors in Markdown format
* `netlab report bgp-neighbor.ascii` creates the Markdown BGP neighbors report and renders it as ASCII text using the [rich.markdown](https://rich.readthedocs.io/en/stable/markdown.html) library.
* `netlab report addressing --node r1,r2` creates addressing report for R1 and R2.

```{tip}
[**netlab inspect** documentation](netlab-inspect-node) describes how to specify the nodes on which the command will be executed.
```
13 changes: 13 additions & 0 deletions netsim/cli/_nodeset.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from box import Box
from ..utils import log
from .. import data
from ..augment import groups

# Is a string a glob expression?
#
Expand All @@ -33,6 +34,15 @@ def add_glob(glob: str, names: list, results: list) -> int:
Given a nodeset (as a string) and the lab topology, return the list
of nodes matching the nodeset. Generate errors as appropriate and
abort if needed.

Nodeset is a comma-separate list of:

* Node names
* Group names
* Glob expressions matching node names

The function does its best to maintain the order in which the nodes
were specified.
"""
def parse_nodeset(ns: str, topology: Box) -> list:
n_names = list(topology.nodes.keys())
Expand All @@ -41,6 +51,9 @@ def parse_nodeset(ns: str, topology: Box) -> list:
if is_glob(n_element):
if not add_glob(n_element,n_names,n_list):
log.error(f'Wildcard node specification {n_element} does not match any nodes',log.IncorrectValue,'')
elif n_element in topology.groups:
node_list = groups.group_members(topology,n_element)
n_list = n_list + [ n for n in node_list if n not in n_list ]
else:
if n_element not in n_names:
log.error(f'Invalid node name {n_element}',log.IncorrectValue,'')
Expand Down
36 changes: 19 additions & 17 deletions netsim/cli/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def exec_parse(args: typing.List[str]) -> typing.Tuple[argparse.Namespace, typin
dest='dry_run',
action='store_true',
help='Print the hosts and the commands that would be executed on them, but do not execute them')
parser.add_argument(
'--header',
dest='header',
action='store_true',
help='Add node headers before command printouts')
parser.add_argument(
dest='node', action='store',
help='Node(s) to run command on')
Expand All @@ -53,21 +58,18 @@ def run(cli_args: typing.List[str]) -> None:

rest = quote_list(rest)
topology = load_snapshot(args)
selector = args.node
args = argparse.Namespace(show=None,verbose=False, quiet=True,Output=True)
if selector in topology.nodes:
connect_to_node(node=selector,args=args,rest=rest,topology=topology,log_level=log_level)
elif selector in topology.groups:
node_list = group_members(topology,selector)
for node in node_list:
connect_to_node(node=node,args=args,rest=rest,topology=topology,log_level=log_level)
else:
node_list = _nodeset.parse_nodeset(selector,topology)
for node in node_list:
connect_to_node(node=node,args=args,rest=rest,topology=topology,log_level=log_level)





node_list = _nodeset.parse_nodeset(args.node,topology)
p_header = args.header

args = argparse.Namespace(show=None,verbose=False,quiet=True,Output=True)
for node in node_list:
if p_header:
print('=' * 80)
print(f'{node}: executing {" ".join(rest)}')
print('=' * 80)
connect_to_node(
node=node,
args=args,
rest=rest,
topology=topology,
log_level=LogLevel.NONE if p_header else log_level)