Skip to content

Start stop nodes#3055

Open
FloSch62 wants to merge 28 commits intomainfrom
start_stop_nodes
Open

Start stop nodes#3055
FloSch62 wants to merge 28 commits intomainfrom
start_stop_nodes

Conversation

@FloSch62
Copy link
Member

@FloSch62 FloSch62 commented Feb 11, 2026

The start/stop/restart command starts/stops/restarts one or more nodes in a deployed lab and restores parked dataplane
interfaces

Tested:

  • nokia_srl
  • nokia_srsim (standalone and distributed)
  • nokia_sros
  • juniper_vmx

Copy link
Collaborator

@steiler steiler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a couple of comments

@FloSch62
Copy link
Member Author

vxlan tests fail, because of #3056

@FloSch62
Copy link
Member Author

FloSch62 commented Feb 13, 2026

For srsim distributed, I may would need some help from you @kaelemc and @sacckth , but imho that can also be added later.

@FloSch62 FloSch62 marked this pull request as ready for review February 13, 2026 14:59
Comment on lines +809 to +814
parkName := clabutils.ParkingNetnsName(cfg.LongName)
parkPath, err := ensureNamedNetns(parkName)
if err != nil {
return fmt.Errorf("node %q failed creating parking netns: %w", cfg.ShortName, err)
}
parkingNode := clablinks.NewGenericLinkNode(parkName, parkPath)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not create a specific ParkingNode... that does all of this.

Comment on lines +818 to +821
d.GetEndpoints(),
func(ep clablinks.Endpoint) error {
return ep.MoveTo(ctx, parkingNode, preMoveSetDownOptions())
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this... via d.GetEndpoints you already have all endpoints at hand. Iterate over these and execute the MoveTo function.... why this weired indirection?!

Comment on lines +826 to +827
if rbErr := rollbackEndpoints(moved, func(ep clablinks.Endpoint) error {
return ep.MoveTo(ctx, d, nil)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, why not call the MoveTo directly...

Comment on lines +852 to +868
parkName := clabutils.ParkingNetnsName(cfg.LongName)
parkPath := filepath.Join("/run/netns", parkName)
if !clabutils.FileOrDirExists(parkPath) {
return fmt.Errorf(
"node %q has no parking netns %q; seamless start requires stopping via containerlab",
cfg.ShortName,
parkName,
)
}
parkingNode := clablinks.NewGenericLinkNode(parkName, parkPath)

// Reparent endpoints to the parking node so MoveTo knows where to find them.
// Stop and Start are separate process invocations, so the in-memory node
// reference from Stop does not survive; we restore it here.
for _, ep := range d.GetEndpoints() {
ep.SetNode(parkingNode)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the parking node implementation you could have a discovery or so to discover all the existing interfaces and simply move all of them back into the given Node, I envision.

Comment on lines +1048 to +1056
func setEndpointsUp(ctx context.Context, endpoints []clablinks.Endpoint) error {
for _, ep := range endpoints {
if err := ep.SetUp(ctx); err != nil {
return err
}
}

return nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a pretty useless use the loop where this is called right now. In my view thats much cleaner.

@kaelemc kaelemc self-assigned this Feb 18, 2026
@kaelemc kaelemc marked this pull request as draft February 18, 2026 15:03
@FloSch62
Copy link
Member Author

FloSch62 commented Mar 3, 2026

@kaelemc and @steiler I tried to drive this further. Feel free to revert the last commit if you had other plans or if its not satisfying to you

@kaelemc kaelemc marked this pull request as ready for review March 5, 2026 00:57
@kaelemc kaelemc requested a review from steiler March 5, 2026 00:57
Comment on lines +156 to +248
func preStopPrepareVrnetlabQcowAlias(ctx context.Context, d *DefaultNode) {
aliasName, ok := vrnetlabQcowAliasName(d.Config().Image)
if !ok {
log.Debugf(
"node %q pre-stop vrnetlab qcow alias skipped: unable to infer tag from image %q",
d.Config().ShortName,
d.Config().Image,
)
return
}

ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()

// Some vrnetlab nodes rename the original versioned qcow image after first boot and fail on
// subsequent starts when they try to rediscover a versioned qcow filename. If there is exactly
// one non-overlay qcow file in / and our alias is absent, create a hardlink alias based on the
// image tag.
cmd := fmt.Sprintf(
`alias="/%s"; `+
`[ -e "$alias" ] && exit 0; `+
`src=""; `+
`if [ -f /sros.qcow2 ] && [ "/sros.qcow2" != "$alias" ]; then `+
`src="/sros.qcow2"; `+
`else `+
`set -- /*.qcow2; `+
`if [ "$1" != "/*.qcow2" ]; then `+
`for f in "$@"; do `+
`[ "$f" = "$alias" ] && continue; `+
`base="${f##*/}"; `+
`case "$base" in *overlay*.qcow2) continue ;; esac; `+
`if [ -n "$src" ]; then src=""; break; fi; `+
`src="$f"; `+
`done; `+
`fi; `+
`fi; `+
`[ -n "$src" ] || exit 0; `+
`ln "$src" "$alias"`,
aliasName,
)

execCmd := clabexec.NewExecCmdFromSlice([]string{"sh", "-lc", cmd})
res, err := d.RunExec(ctx, execCmd)
if err != nil {
log.Warnf(
"node %q pre-stop vrnetlab qcow alias preparation failed: %v",
d.Config().ShortName,
err,
)
return
}

if res != nil && res.ReturnCode != 0 {
log.Warnf(
"node %q pre-stop vrnetlab qcow alias prep returned code %d (stderr: %s)",
d.Config().ShortName,
res.ReturnCode,
res.Stderr,
)
}
}

func vrnetlabQcowAliasName(image string) (string, bool) {
tag, ok := imageTag(image)
if !ok {
return "", false
}

return "clab-" + tag + ".qcow2", true
}

func imageTag(image string) (string, bool) {
if at := strings.LastIndex(image, "@"); at != -1 {
image = image[:at]
}

lastSlash := strings.LastIndex(image, "/")
lastColon := strings.LastIndex(image, ":")
if lastColon == -1 || lastColon < lastSlash {
return "", false
}

tag := image[lastColon+1:]
if tag == "" {
return "", false
}

if !imageTagRE.MatchString(tag) {
return "", false
}

return tag, true
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure if we want all this in here. I feel maybe it can be something that lives in the vrnetlab side or something, but it would mean people have to build new images.

@codecov
Copy link

codecov bot commented Mar 5, 2026

Codecov Report

❌ Patch coverage is 48.33703% with 233 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.42%. Comparing base (fb0bcf1) to head (f1be230).
⚠️ Report is 16 commits behind head on main.

Files with missing lines Patch % Lines
nodes/vr_node.go 0.00% 70 Missing ⚠️
nodes/default_node.go 40.62% 28 Missing and 10 partials ⚠️
nodes/sros/sros.go 26.92% 19 Missing ⚠️
mocks/mocknodes/node.go 0.00% 16 Missing ⚠️
links/parking_node.go 70.83% 8 Missing and 6 partials ⚠️
runtime/podman/podman.go 0.00% 12 Missing ⚠️
utils/netlink.go 67.56% 10 Missing and 2 partials ⚠️
core/restart.go 33.33% 4 Missing and 4 partials ⚠️
mocks/mocknodes/default_node.go 0.00% 8 Missing ⚠️
core/lifecycle.go 68.42% 3 Missing and 3 partials ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3055      +/-   ##
==========================================
- Coverage   56.92%   56.42%   -0.50%     
==========================================
  Files         216      224       +8     
  Lines       21469    22019     +550     
==========================================
+ Hits        12221    12425     +204     
- Misses       8003     8304     +301     
- Partials     1245     1290      +45     
Files with missing lines Coverage Δ
cmd/options.go 91.23% <100.00%> (+0.04%) ⬆️
cmd/root.go 81.06% <100.00%> (+2.44%) ⬆️
core/destroy.go 59.21% <100.00%> (+0.72%) ⬆️
nodes/ceos/ceos.go 64.10% <100.00%> (+0.18%) ⬆️
nodes/linux/linux.go 79.54% <100.00%> (+0.47%) ⬆️
nodes/node.go 21.42% <ø> (ø)
nodes/srl/srl.go 62.17% <100.00%> (+0.22%) ⬆️
runtime/runtime.go 63.82% <ø> (ø)
types/types.go 50.00% <ø> (ø)
runtime/docker/docker.go 66.94% <89.47%> (+0.93%) ⬆️
... and 16 more

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants