Skip to content

netlab status: show memory usage per node and per lab - #3794

Merged
ipspace merged 2 commits into
ipspace:devfrom
sdargoeuves:memory_usage
Aug 20, 2026
Merged

netlab status: show memory usage per node and per lab#3794
ipspace merged 2 commits into
ipspace:devfrom
sdargoeuves:memory_usage

Conversation

@sdargoeuves

Copy link
Copy Markdown
Collaborator

As it has been discussed on the Network Automagic podcast, I wanted to give (AI a) go at showing the memory usage of a lab, per node.

This is the output for a lab with libvirt+clab:

Lab 2 in /home/sa/code/quick-netlab-lab/dns-lab
  status:      started
  topology:    topology.yml
  provider(s): libvirt,clab
  memory used: 1.838GiB

┏━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ node   ┃ device  ┃ mgmt IP       ┃ provider ┃ status        ┃ memory   ┃
┡━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ r1     │ arubacx │ 10.194.57.101 │ libvirt  │ running       │ 1.768GiB │
│ r2     │ frr     │ 10.194.57.102 │ clab     │ Up 11 minutes │ 30.56MiB │
│ r3     │ frr     │ 10.194.57.103 │ clab     │ Up 11 minutes │ 33.27MiB │
│ dnssrv │ dnsmasq │ 10.194.57.104 │ clab     │ Up 11 minutes │ 7.867MiB │
└────────┴─────────┴───────────────┴──────────┴───────────────┴──────────┘

Disclosure: high risk of AI slop from this point.

Implementation — each provider's get_lab_status enriches its status box with a memory key; fetch_node_status copies it into the node status and sums the total into lab_state.memory (so it also lands in --format json/yaml).

  • clab: docker stats --no-stream --format jsonMemUsage, "used" half only. The value after the slash is the host total when no limit is set (always, for netlab-generated labs), so it's dropped.
  • libvirt: virsh domstats --balloon --list-runningballoon.rss (host RSS of the QEMU process). Falls back to balloon.current suffixed (max) when the guest has no balloon driver. Domain names are matched back to Vagrant machine names via the _<node> suffix; no new keys are added to the status box.
  • strings.py: format_memory_size / parse_memory_size helpers, docker's 1.521GiB / 184.5MiB style.

Both collectors are non-fatal — failures are print_verbose only, memory is extra info.

Caveats / things you may want changed:

  • Adds ~1.5–2s to netlab status for a clab lab (docker stats --no-stream samples every container on the host, not just the lab's).
  • The two numbers aren't the same measure: cgroup footprint vs QEMU RSS, so the total sums slightly different things. It's the right ballpark for "what is this lab eating", not exact host-RAM accounting. With KSM enabled, summing RSS overstates real usage. whatever that means 🫥
  • No used / allocated display, which could be useful for libvirt.

@jbemmel

jbemmel commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

I contributed a PR to Containerlab over the weekend: srl-labs/containerlab#3325

These cgroups allow you to collect lots of stats directly:

root ➜ /xform $ cat /sys/fs/cgroup/xform/ospf-dual-stack/r1/memory.peak 
62664704
root ➜ /xform $ cat /sys/fs/cgroup/xform/ospf-dual-stack/r1/memory.current 
46010368

I suspect Linux sys/fs could give us all those numbers without going through those separate tools

@ipspace

ipspace commented Aug 17, 2026

Copy link
Copy Markdown
Owner

These cgroups allow you to collect lots of stats directly:

Just to make sure I understand: when your PR gets into a containerlab release, and when we switch to that release, and (optionally) when we update the clab.yml, we will be able to read these things directly.

Right?

@jbemmel

jbemmel commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

These cgroups allow you to collect lots of stats directly:

Just to make sure I understand: when your PR gets into a containerlab release, and when we switch to that release, and (optionally) when we update the clab.yml, we will be able to read these things directly.

Right?

Well, actually we could read it today:

docker inspect --format '{{.State.Pid}}' <container name> gives the PID
cat /proc/<PID>/cgroup gives the cgroup
cat /sys/fs/cgroup/<cgroup>/memory.current gives current memory usage

The cgroup parent grouping can be helpful to get the aggregate, and makes the lookup slightly simpler.

@sdargoeuves

Copy link
Copy Markdown
Collaborator Author

Aaah, I forgot to remove my Multilab commit... I'll remove it later.

I didn't know about cgroup I recently wanted to check how much RAM were the SDWAN components eating, and started looking into the docker stats command.

@sdargoeuves

Copy link
Copy Markdown
Collaborator Author

We could also use the cgroup approach for libvirt nodes, if we want to go that direction:

╰─❯ cat /run/libvirt/qemu/ml-2_aruba0.pid -p                                                   
1997096
╰─❯ cat /proc/1997096/cgroup -p                                                                
0::/machine.slice/machine-qemu\x2d2\x2dml\x2d2aruba0.scope/libvirt/emulator
╰─❯ cat /sys/fs/cgroup/machine.slice/machine-qemu\\x2d2\\x2dml\\x2d2aruba0.scope/memory.current -p
1977995264

@ipspace

ipspace commented Aug 18, 2026

Copy link
Copy Markdown
Owner

Well, actually we could read it today:

Yeah, I found that recipe, and figured out it does not work on my Docker/Ubuntu combo 🤷‍♂️

@sdargoeuves -- I would suggest we use the orchestration tools (Docker/libvirt) to fetch the stats. Let them deal with the underlying OS mess. See also: https://www.youtube.com/watch?v=oebqlzblfyo

@sdargoeuves

Copy link
Copy Markdown
Collaborator Author

Finally got a chance to start my main lab to look at the memory usage with this branch. The netlab status command takes 46s with this branch, compared to 44s when using the August release.

Now I can see which node(s) are the hungry ones... no surprises, but still nice to see!

Here is an extract:

╰─❯ dnetlab status              
Lab 1 in /home/sa/code/netsim-main-lab
  status:      started
  topology:    topology.yml
  provider(s): libvirt,clab
  memory used: 153.403GiB

┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ node             ┃ device   ┃ image                                      ┃ mgmt IP       ┃ connection  ┃ provider ┃ VM/container               ┃ status                  ┃ memory   ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ d4xfw01          │ linux    │ checkpoint/cloudguard:81.20.5699           │ 10.194.56.99  │ paramiko    │ libvirt  │ ml-1_d4xfw01               │ inaccessible            │ 6.580GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ lb3x01           │ linux    │ f5/bigip                                   │ 10.194.56.101 │ paramiko    │ libvirt  │ ml-1_lb3x01                │ running                 │ 4.009GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ lb3x02           │ linux    │ netlab/netscaler                           │ 10.194.56.102 │ docker      │ clab     │ clab-ml-1-lb3x02           │ Up 51 minutes           │ 567MiB   │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ sdwan-manager    │ linux    │ vrnetlab/cisco_sdwan-manager:20.16.1       │ 10.194.56.130 │ docker      │ clab     │ clab-ml-1-sdwan-manager    │ Up 51 minutes (healthy) │ 32.25GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ sdwan-controller │ linux    │ vrnetlab/cisco_sdwan-controller:20.16.1    │ 10.194.56.131 │ docker      │ clab     │ clab-ml-1-sdwan-controller │ Up 51 minutes (healthy) │ 993.6MiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ sdwan-validator  │ linux    │ vrnetlab/cisco_sdwan-validator:20.16.1     │ 10.194.56.132 │ docker      │ clab     │ clab-ml-1-sdwan-validator  │ Up 51 minutes (healthy) │ 831.7MiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ edge1x01         │ cat8000v │ vrnetlab/cisco_c8000v:controller-17.15.04c │ 10.194.56.133 │ network_cli │ clab     │ clab-ml-1-edge1x01         │ Up 51 minutes (healthy) │ 4.029GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ edge2x01         │ cat8000v │ vrnetlab/cisco_c8000v:controller-17.15.04c │ 10.194.56.134 │ network_cli │ clab     │ clab-ml-1-edge2x01         │ Up 51 minutes (healthy) │ 4.028GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ edge3x01         │ cat8000v │ vrnetlab/cisco_c8000v:controller-17.15.04c │ 10.194.56.135 │ network_cli │ clab     │ clab-ml-1-edge3x01         │ Up 51 minutes (healthy) │ 4.029GiB │
├──────────────────┼──────────┼────────────────────────────────────────────┼───────────────┼─────────────┼──────────┼────────────────────────────┼─────────────────────────┼──────────┤
│ c0xr01           │ vsrx     │ juniper/vsrx3:21.3R1.9                     │ 10.194.56.66  │ netconf     │ libvirt  │ ml-1_c0xr01                │ running                 │ 3.990GiB │
[...]

@sdargoeuves
sdargoeuves marked this pull request as ready for review August 19, 2026 23:05
@sdargoeuves

sdargoeuves commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator Author

use the orchestration tools (Docker/libvirt) to fetch the stats.

I won't push the idea of the /sys/fs/ approach, I did a local test, working for both docker and libvirt. Now I have no idea why it didn't work on your instance to check for the docker memory usage... 🤷‍♂️
Hopefully in it's current state, it should work for you as it's relying on docker and libvirt stats.

@ipspace

ipspace commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Finally got a chance to start my main lab to look at the memory usage with this branch. The netlab status command takes 46s with this branch, compared to 44s when using the August release.

On my simple containers-only lab, it took 2.3 seconds instead of 0.3 seconds, so it looks like the 2-second add-on is pretty much constant?

Should we add a '--memory' flag to trigger the memory usage collection, or just make it a default and accept the 2-second delay?

@ipspace

ipspace commented Aug 20, 2026

Copy link
Copy Markdown
Owner

use the orchestration tools (Docker/libvirt) to fetch the stats.

I won't push the idea of the /sys/fs/ approach, I did a local test, working for both docker and libvirt.

Now it works for me as well (after I ran your code which ran docker stats)... or maybe I was just brain-dead.

Anyway, if we use docker stats and virsh domstats, and things don't work as expected, it's someone else's problem. If we start using /sys/fs, it becomes ours, and it could turn into a whack-a-mole situation.

@sdargoeuves

Copy link
Copy Markdown
Collaborator Author

On my main lab, that command is so slow, adding 2min is not an issue. But yes, adding a --memory option would be a nice way of making it optional

@ipspace ipspace left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The code looks good to me, the only thing left is the question of whether we accept the 2-second delay of docker stats, or make this optional, or add a "collecting memory usage, please wait" line to the interactive printout (I can easily add one or the other; just let me know what you prefer).

@DanPartelly

Copy link
Copy Markdown
Collaborator

The code looks good to me, the only thing left is the question of whether we accept the 2-second delay of **docker

My two cents: everything should run as fast as possible. It's 2026. Memory consumption is largely irrelevant in day to day operations for a user. You'll know when you exceed available memory: OOM killer will ruin your lab. Due to the nature of the beast, it's also something you can do nothing about.

--top or --memory is ideal.

@DanPartelly

Copy link
Copy Markdown
Collaborator

If you have no other containers running:

systemd-cgtop -m -b -n 1 -r /system.slice | grep -E '^[[:space:]]*/system.slice/docker-[a-f0-9]+.scope[[:space:]]' | awk '{sum+=$4} END {print sum/1024/1024 " MB total"}'

@ipspace
ipspace merged commit 6aa21f1 into ipspace:dev Aug 20, 2026
4 checks passed
@ipspace

ipspace commented Aug 20, 2026

Copy link
Copy Markdown
Owner

@sdargoeuves -- I decided to merge this and then add bells and whistles. Thanks again, a wonderful addition ;)

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.

4 participants