diff --git a/clab/clab.go b/clab/clab.go index a5be8a06f..88c94ba30 100644 --- a/clab/clab.go +++ b/clab/clab.go @@ -1170,11 +1170,6 @@ func (c *CLab) Version(ctx context.Context) error { return nil } -// Inspect the given topology -func (c *CLab) Inspect(ctx context.Context) error { - return nil -} - // Save configuration of the given topology nodes func (c *CLab) Save(ctx context.Context) error { return nil @@ -1186,8 +1181,53 @@ func (c *CLab) Graph(ctx context.Context) error { } // Exec execute commands on running topology nodes -func (c *CLab) Exec(ctx context.Context) error { - return nil +func (c *CLab) Exec(ctx context.Context, cmds []string, options *ExecOptions) (*exec.ExecCollection, error) { + + err := links.SetMgmtNetUnderlayingBridge(c.Config.Mgmt.Bridge) + if err != nil { + return nil, err + } + + cnts, err := c.ListContainers(ctx, options.filters) + if err != nil { + return nil, err + } + + // make sure filter returned containers + if len(cnts) == 0 { + return nil, fmt.Errorf("filter did not match any containers") + } + + // prepare the exec collection and the exec command + resultCollection := exec.NewExecCollection() + + // build execs from the string input + var execCmds []*exec.ExecCmd + for _, execCmdStr := range cmds { + execCmd, err := exec.NewExecCmdFromString(execCmdStr) + if err != nil { + return nil, err + } + execCmds = append(execCmds, execCmd) + } + + // run the exec commands on all the containers matching the filter + for _, cnt := range cnts { + // iterate over the commands + for _, execCmd := range execCmds { + // execute the commands + execResult, err := cnt.RunExec(ctx, execCmd) + if err != nil { + // skip nodes that do not support exec + if err == exec.ErrRunExecNotSupported { + continue + } + } + resultCollection.Add(cnt.Names[0], execResult) + } + } + + return resultCollection, nil } // Configure topology nodes diff --git a/clab/exec_options.go b/clab/exec_options.go new file mode 100644 index 000000000..e6c099469 --- /dev/null +++ b/clab/exec_options.go @@ -0,0 +1,17 @@ +package clab + +import "github.com/srl-labs/containerlab/types" + +type ExecOptions struct { + filters []*types.GenericFilter +} + +func NewExecOptions(filters []*types.GenericFilter) *ExecOptions { + return &ExecOptions{ + filters: []*types.GenericFilter{}, + } +} + +func (e *ExecOptions) AddFilters(f ...*types.GenericFilter) { + e.filters = append(e.filters, f...) +} diff --git a/cmd/exec.go b/cmd/exec.go index 046dbe5aa..c35112550 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -13,7 +13,6 @@ import ( "github.com/srl-labs/containerlab/clab" "github.com/srl-labs/containerlab/clab/exec" "github.com/srl-labs/containerlab/labels" - "github.com/srl-labs/containerlab/links" "github.com/srl-labs/containerlab/runtime" "github.com/srl-labs/containerlab/types" ) @@ -33,6 +32,10 @@ var execCmd = &cobra.Command{ } func execFn(_ *cobra.Command, _ []string) error { + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + if len(execCommands) == 0 { return errors.New("provide command to execute") } @@ -63,23 +66,15 @@ func execFn(_ *cobra.Command, _ []string) error { clab.WithDebug(debug), ) - c, err := clab.NewContainerLab(opts...) - if err != nil { - return err + if name != "" { + opts = append(opts, clab.WithLabName(name)) } - err = links.SetMgmtNetUnderlayingBridge(c.Config.Mgmt.Bridge) + c, err := clab.NewContainerLab(opts...) if err != nil { return err } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - if name == "" { - name = c.Config.Name - } - var filters []*types.GenericFilter if len(labelsFilter) != 0 { @@ -91,45 +86,7 @@ func execFn(_ *cobra.Command, _ []string) error { filters = append(filters, types.FilterFromLabelStrings(labFilter)...) } - // list all containers using global runtime using provided filters - cnts, err := c.GlobalRuntime().ListContainers(ctx, filters) - if err != nil { - return err - } - - // make sure filter returned containers - if len(cnts) == 0 { - return fmt.Errorf("filter did not match any containers") - } - - // prepare the exec collection and the exec command - resultCollection := exec.NewExecCollection() - - // build execs from the string input - var execCmds []*exec.ExecCmd - for _, execCmdStr := range execCommands { - execCmd, err := exec.NewExecCmdFromString(execCmdStr) - if err != nil { - return err - } - execCmds = append(execCmds, execCmd) - } - - // run the exec commands on all the containers matching the filter - for _, cnt := range cnts { - // iterate over the commands - for _, execCmd := range execCmds { - // execute the commands - execResult, err := cnt.RunExec(ctx, execCmd) - if err != nil { - // skip nodes that do not support exec - if err == exec.ErrRunExecNotSupported { - continue - } - } - resultCollection.Add(cnt.Names[0], execResult) - } - } + resultCollection, err := c.Exec(ctx, execCommands, clab.NewExecOptions(filters)) switch outputFormat { case exec.ExecFormatPlain: diff --git a/cmd/inspect.go b/cmd/inspect.go index cc84df094..37607a6c5 100644 --- a/cmd/inspect.go +++ b/cmd/inspect.go @@ -50,6 +50,10 @@ func inspectFn(_ *cobra.Command, _ []string) error { fmt.Println("provide either a lab name (--name) or a topology file path (--topo) or the --all flag") return nil } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + opts := []clab.ClabOption{ clab.WithTimeout(timeout), clab.WithRuntime(rt, @@ -69,23 +73,20 @@ func inspectFn(_ *cobra.Command, _ []string) error { ) } + if name != "" { + opts = append(opts, clab.WithLabName(name)) + } + c, err := clab.NewContainerLab(opts...) if err != nil { return fmt.Errorf("could not parse the topology file: %v", err) } - if name == "" { - name = c.Config.Name - } - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - var containers []runtime.GenericContainer // if the topo file is available, use it if topo != "" { - containers, err = c.listNodesContainers(ctx) + containers, err = c.ListContainers(ctx, nil) if err != nil { return fmt.Errorf("failed to list containers: %s", err) }