Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow combination of filter and topology in exec command #1729

Merged
merged 5 commits into from
Nov 22, 2023
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
26 changes: 18 additions & 8 deletions cmd/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ func execFn(_ *cobra.Command, _ []string) error {
return err
}

opts := []clab.ClabOption{
opts := make([]clab.ClabOption, 0, 5)

// exec can work with or without a topology file
// when topology file is provided we need to parse it
// when topo file is not provided, we rely on labels to perform the filtering
if topo != "" {
opts = append(opts, clab.WithTopoPath(topo, varsFile))
}

opts = append(opts,
clab.WithTimeout(timeout),
clab.WithTopoPath(topo, varsFile),
clab.WithNodeFilter(nodeFilter),
clab.WithRuntime(rt,
&runtime.RuntimeConfig{
Expand All @@ -54,7 +62,8 @@ func execFn(_ *cobra.Command, _ []string) error {
},
),
clab.WithDebug(debug),
}
)

c, err := clab.NewContainerLab(opts...)
if err != nil {
return err
Expand All @@ -73,13 +82,14 @@ func execFn(_ *cobra.Command, _ []string) error {
}

var filters []*types.GenericFilter
switch {
case len(labelsFilter) != 0:

if len(labelsFilter) != 0 {
filters = types.FilterFromLabelStrings(labelsFilter)
default:
// when user-defined labels are not provided we should filter the nodes of the lab
}

if topo != "" {
labFilter := []string{fmt.Sprintf("%s=%s", labels.Containerlab, c.Config.Name)}
filters = types.FilterFromLabelStrings(labFilter)
filters = append(filters, types.FilterFromLabelStrings(labFilter)...)
}

// list all containers using global runtime using provided filters
Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func preRunFn(cmd *cobra.Command, _ []string) error {
func getTopoFilePath(cmd *cobra.Command) error {
// set commands which may use topo file find functionality, the rest don't need it
if !(cmd.Name() == "deploy" || cmd.Name() == "destroy" || cmd.Name() == "inspect" ||
cmd.Name() == "save" || cmd.Name() == "graph" || cmd.Name() == "exec") {
cmd.Name() == "save" || cmd.Name() == "graph") {
return nil
}

Expand Down
71 changes: 45 additions & 26 deletions docs/cmd/exec.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
# exec command

### Description
## Description

The `exec` command allows to run a command inside the nodes that part of a certain lab.
The `exec` command allows to execute a command inside the nodes (containers).

This command does exactly the same thing as `docker exec` does, but it allows to run the same command across all the nodes of a lab.
This command is similar to `docker exec`, but it allows to run the same command across multiple lab nodes using filters. Users can provide a path to the topology file and `--label` filter to narrow down the list of nodes to execute the command on.

### Usage
## Usage

`containerlab [global-flags] exec [local-flags]`

### Flags
## Flags

#### topology
### topology

With the global `--topo | -t` flag a user sets the path to the topology definition file that will be used to spin up a lab.
With the global `--topo | -t` flag a user can set a path to the topology file that will be used to filter the nodes targeted for execution of the command. The nodes can further be filtered with the `--label` flag.

When the topology path refers to a directory, containerlab will look for a file with `.clab.yml` extension in that directory and use it as a topology definition file.
Note, that with the nodes of [`ext-container` type](../manual/kinds/ext-container.md), the topology must not be provided.

When the topology file flag is omitted, containerlab will try to find the matching file name by looking at the current working directory.

If more than one file is found for directory-based path or when the flag is omitted entirely, containerlab will fail with an error.

#### cmd
### cmd

The command to be executed on the nodes is provided with `--cmd` flag. The command is provided as a string, thus it needs to be quoted to accommodate for spaces or special characters.

#### format
### format

The `--format | -f` flag allows to select between plain text format output or a json variant. Consult with the examples below to see the differences between these two formatting options.
The `--format | -f` flag allows selecting between plain text format output or a json variant. Consult with the examples below to see the differences between these two formatting options.

Defaults to `plain` output format.

#### label
### label

Using `--laber` it is possible to filter the nodes to execute the command on using labels attached to the nodes. The label is provided as a string in the form of `key=value`. The `key` is the label name, and the `value` is the label value.

Exec command should either be provided with a topology file, or labels, or both.

By default `exec` command will attempt to execute the command across all the nodes of a lab. To limit the scope of the execution, the users can leverage the `--label` flag to filter out the nodes of interest.
Recall that you can check the labels attached to the nodes with `docker inspect -f '{{.Config.Labels | json}}' <container-name>` command.

#### node-filter
### node-filter

The local `--node-filter` flag allows a user to specify a subset of nodes from the topology to exec the command(s) on, instead of all (default). Applies to executions where the topology file is provided.

### Examples
## Examples

#### Execute a command on all nodes of the lab
### Execute a command on all nodes of the lab

Show ipv4 information from all the nodes of the lab with a plain text output
Show ipv4 information from all the nodes of the lab defined in `srl02.clab.yml` with a plain text output

```bash
❯ containerlab exec -t srl02.yml --cmd 'ip -4 a show dummy-mgmt0'
❯ containerlab exec -t srl02.clab.yml --cmd 'ip -4 a show dummy-mgmt0'
INFO[0000] clab-srl02-srl1: stdout:
6: dummy-mgmt0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
inet 172.20.20.3/24 brd 172.20.20.255 scope global dummy-mgmt0
Expand All @@ -58,20 +58,36 @@ INFO[0000] clab-srl02-srl2: stdout:
valid_lft forever preferred_lft forever
```

#### Execute a command on a node referenced by its name
### Execute a command on a node referenced by its name

Show ipv4 information from a specific node of the lab with a plain text output

```bash
❯ containerlab exec -t srl02.yml --label clab-node-name\=srl2 --cmd 'ip -4 a show dummy-mgmt0'
❯ containerlab exec -t srl02.clab.yml --label clab-node-name=srl2 --cmd 'ip -4 a show dummy-mgmt0'
INFO[0000] Parsing & checking topology file: srl02.yml
INFO[0000] Executed command 'ip -4 a show dummy-mgmt0' on clab-srl02-srl2. stdout:
6: dummy-mgmt0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
inet 172.20.20.5/24 brd 172.20.20.255 scope global dummy-mgmt0
valid_lft forever preferred_lft forever
```

#### Execute a CLI Command
### Execute a command on multiple nodes referenced by a filter and no topology file

Since containerlab injects default labels to the nodes, it is possible to leverage `clab-node-kind` label that is attached to all the nodes. This label contains the node kind (type) information. In this example we will execute a command on all the nodes of the lab that are of `nokia_srlinux` kind.

```bash
❯ sudo clab exec --label clab-node-kind=nokia_srlinux --cmd "ip -4 addr show dummy-mgmt0"
INFO[0000] Executed command "ip -4 addr show dummy-mgmt0" on the node "greeter-srl". stdout:
2: dummy-mgmt0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
inet 172.20.20.2/24 brd 172.20.20.255 scope global dummy-mgmt0
valid_lft forever preferred_lft forever
INFO[0000] Executed command "ip -4 addr show dummy-mgmt0" on the node "srl". stdout:
2: dummy-mgmt0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
inet 172.20.20.3/24 brd 172.20.20.255 scope global dummy-mgmt0
valid_lft forever preferred_lft forever
```

### Execute a CLI Command

```bash
❯ containerlab exec -t srl02.yml --cmd 'sr_cli "show version"'
Expand Down Expand Up @@ -105,10 +121,13 @@ Free Memory : 21911914 kB
----------------------------------------------------
```

#### Execute a Command with json formatted output
### Execute a Command with json formatted output

```bash
❯ containerlab exec -t srl02.yml --cmd 'sr_cli "show version | as json"' -f json | jq
```

```json
{
"clab-srl02-srl1": {
"stderr": "",
Expand Down
8 changes: 4 additions & 4 deletions tests/06-ext-container/01-ext-container.robot
Original file line number Diff line number Diff line change
Expand Up @@ -29,28 +29,28 @@ Deploy ${lab-name} lab

Verify links in node ext1
${rc} ${output} = Run And Return Rc And Output
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=ext1 --cmd "ip link show dev eth1"
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec --label clab-node-name\=ext1 --cmd "ip link show dev eth1"
Log ${output}
Should Be Equal As Integers ${rc} 0
Should Contain ${output} state UP

Verify ip and thereby exec on ext1
${rc} ${output} = Run And Return Rc And Output
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=ext1 --cmd "ip address show dev eth1"
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec --label clab-node-name\=ext1 --cmd "ip address show dev eth1"
Log ${output}
Should Be Equal As Integers ${rc} 0
Should Contain ${output} 192.168.0.1/24

Verify links in node ext2
${rc} ${output} = Run And Return Rc And Output
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=ext2 --cmd "ip link show dev eth1"
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec --label clab-node-name\=ext2 --cmd "ip link show dev eth1"
Log ${output}
Should Be Equal As Integers ${rc} 0
Should Contain ${output} state UP

Verify ip and thereby exec on ext2
${rc} ${output} = Run And Return Rc And Output
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=ext2 --cmd "ip address show dev eth1"
... sudo -E ${CLAB_BIN} --runtime ${runtime} exec --label clab-node-name\=ext2 --cmd "ip address show dev eth1"
Log ${output}
Should Be Equal As Integers ${rc} 0
Should Contain ${output} 192.168.0.2/24
Expand Down