Straight to the list, live, once a machine is being made.
Starting a clone left a sentence on the template's sheet saying that its TASK
column shows how far along it is and that ^l would make that move — three steps
of housekeeping after the one decision that mattered, on a screen which is a
page of facts about the machine being copied rather than about the one being
made.
Now the sheet goes away, the list comes back with the cursor still on the row
the task hangs off, and live mode turns itself on where it was off. The
percentage moves by itself, the new machine turns up in the table when it
exists, and the line under the table announces it.
web05 is being made at 141.14.140.182 — live on, ubuntu-tpl shows how far
along it is
Live is said in that line because it is a mode and it stays on: the title says
it is running and ^l ends it. It does not sweep at once the way ^l does — the
deployment re-read its own row a moment earlier — so the first tick is one
interval away, and that interval is already the busy one, because the row it
re-read is the one carrying the clone.
Putting the sheet away is a function of its own now: Esc and this both have to
forget the events that were read during the visit, and that reason belongs in
one place.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gvm — VMware command line helper
A small command line tool for the VMware vCenters: list the virtual machines of all of them at once, take and remove snapshots, look at what the ESXi hosts and the datastores are doing, report the snapshots nobody came back for, and mail the vCenter event log.
gvm # interactive list of every machine, everywhere
gvm vm # the same thing, spelled out
gvm vm -l # the same as plain output
gvm vm -l -m web # only those whose name matches "web"
gvm vm -l --issues # only the machines with something wrong
gvm vm -l --json # the same listing as a document
gvm -v v108 snap -l myvm # the snapshots of myvm on v108
gvm -v v108 snap -n myvm # take one
gvm snap --old # every snapshot older than 30 days, everywhere
gvm -v v108 power -s myvm # ask its guest to shut down
gvm host # cpu, memory and machine counts per host
gvm ds # capacity, free space and over-commitment
gvm log -l # the last hour of events
gvm config # what gvm made of ~/.gvmrc
Configuration
Everything gvm knows about the world is in ~/.gvmrc; there are no built-in
servers and no built-in credentials. On first run gvm writes an annotated
template there and says so — fill in the passwords and it works.
gvmrc.example is the same file with the comments spelled out.
default = v308
vcenter.v308.url = https://v308.fhi.mpg.de/
vcenter.v308.user = administrator@v308.fhi.mpg.de
vcenter.v308.password = ...
vcenter.v308.datacenter = PPB
vcenter.v308.insecure = true
-v <name> picks a server by its full name; an unknown name is an error rather
than a silent fallback to the first one in the list. The commands that sweep
every server — gvm, gvm vm -l, gvm snap --old — also take a list,
-v v308,v108, in the order given and with a repeat counted once; the commands
that act on one machine refuse a list rather than taking the first of it. Every
setting has an environment spelling that wins over the file — GVM_VCENTER_V308_PASSWORD,
GVM_MAILTO, GVM_DEFAULT and so on — which is how to run gvm from cron
without the password living in a file.
Besides the servers it holds the site's own answers, each written once: where
mail goes, where telemetry is posted, how h logs in to a guest, and what a
machine made from a template is told about the network (domain, dns,
netmask, gateway, timezone — see Making a machine from a template).
gvm config prints the lot, passwords as set or not set, and names what is
missing for anything that is half configured.
The file holds passwords, so gvm creates it mode 0600 and complains when it finds it readable by others.
Passwords in the file
A password written into ~/.gvmrc in the clear is sealed on the next run of gvm
and replaced in place by a gvmenc1:... word:
vcenter.v308.password = gvmenc1:otspj7CLz1/8vEUFHpfrKH/zKiVLqlOvVwQ…
Nothing else about the file changes — the keys, the spacing, the order, the blank
lines and the comments beside a setting are all left exactly as they were — and
gvm says which password it sealed. Only the value is sealed, never the file, so
~/.gvmrc stays readable and editable by hand.
gvm config -p v308 asks for a password instead of taking it from the file and
writes it sealed straight away. That is the way to set one: a password typed into
the file stands there in the clear until the next run of gvm, and by then it has
been through the editor's swap file and whatever backs the home directory up.
gvm config says whether each password is sealed, still in the clear, or sealed
but no longer openable — it opens each one and throws it away, because "it is
sealed" is worth nothing if it does not open.
What this is, and is not. The key is compiled into gvm and is the same in
every copy of it, so whoever holds ~/.gvmrc and a gvm binary can open the
value; prising the key out is an afternoon's work, not a cluster's. This is not a
vault. What it buys is that the password no longer stands in the clear in a
backup, in a home directory that syncs somewhere, in an editor's swap file, or on
a screen someone else is looking at. The 0600 is what keeps other local users
out. A value given in GVM_VCENTER_*_PASSWORD is taken as it stands, sealed or
not.
Commands
| command | what it does |
|---|---|
| (nothing) | browse the machines interactively (see below) |
vm |
the same, spelled out |
vm -l [-m <re>] [--sort <order>] [--reverse] |
print them instead; all vCenters unless -v names one |
vm -l --issues |
only the machines with something wrong with them |
vm -l --json |
the same listing as a JSON document |
snap -l <vm> |
list a machine's snapshots |
snap -n <vm> |
take a snapshot, name printed |
snap --old [-d <days>] [-m] |
every snapshot older than that, on every vCenter, optionally by mail |
snap -r <vm> -s <snap> |
remove one snapshot |
snap --revert <vm> -s <snap> |
put the machine back to that snapshot |
snap --removeall <vm> |
remove all of them |
power -o <vm> |
power on |
power -s <vm> |
ask the guest to shut down (needs VMware Tools) |
power -b <vm> |
ask the guest to reboot (needs VMware Tools) |
power --off <vm> |
power off at the hypervisor — hard |
power --reset <vm> |
reset at the hypervisor — hard |
size --vm <vm> |
what it has: vCPUs, memory, and what may be changed while it runs |
size --vm <vm> -c 8 -m 16 |
give it 8 vCPUs and 16 GB — -m 512m for megabytes |
new --from <template> --name <vm> |
make a new machine from a template |
new ... --host <h> --datastore <ds> --on |
where it lands, and whether it starts |
new ... --spec <spec> --ip <addr> |
customise the guest: hostname and address |
new ... --ip auto |
have the site's dns tool hand an address out |
new --specs |
the customisation specifications this vCenter holds |
config |
also shows what a new guest would be told about the network |
host [-t] |
per-host cpu, memory, machine counts; -t also posts them |
host -c |
just the machine counts |
ds [-t] |
per-datastore capacity, free space, over-commitment; -t also posts them |
log -l [-m] [-t <min>] |
the event log, optionally by mail, default 60 minutes |
config |
the effective configuration, passwords not shown |
config -p <vcenter> |
ask for a password and store it sealed |
completion zsh|bash |
print the shell completion script |
The interactive list
gvm on its own — or gvm vm, or gvm vm -i — asks every configured vCenter
at once and puts the machines of all of them into one full-screen list, sorted
by name. -v <name> narrows it to one server, and gvm -h still prints the
help, as does anything gvm does not recognise:
type narrow the list — the text is matched against the whole
line, so a name, an address, a host or "off" all work, and
the hit is picked out in the row
↑ ↓ PgUp PgDn move, Home/End for the ends
enter the machine's parameters: what is wrong with it and what is
being done to it, power, host, guest and tools, cpu and
memory in use, uptime, storage, guest filesystems, network
adapters, snapshots, uuid and moref
↑ ↓ in there scroll the sheet, esc/enter back to the list
e h y w in the sheet: recent events, ssh to the guest, copy what
ssh would connect to, open it in the vSphere client
The sheet is one line per thing worth knowing, values that belong together joined with a middle dot and no section headings — an ordinary machine fits a 24-row terminal whole, and the identity numbers and the annotation at the bottom are the only part anyone scrolls for. A value too long for the width is carried onto a continuation line under its own label rather than cut off at the edge, including one long word such as a datastore path, so a narrow terminal loses nothing. The machine's name, vCenter, datacenter and host are the title. ^o sort the table (see below) ^w only the machines with something wrong with them (see below) ^e the whole estate on one screen (see below) ^l live: the list re-reads itself (see below) ^r ask the servers again esc clear the filter, or leave when there is none ^c leave
The columns are name, vCenter, power, snapshots, address, host, vCPUs, CPU load, memory and memory in use, and the guest's operating system. The two load figures are percentages of what the machine is allowed to use and of what it has configured; a machine that is not running has no load rather than a load of zero and shows a dash. Past 75 % they turn yellow, past 90 % red.
The snapshot column is how many the machine is dragging along, aged by colour:
past a week the count turns yellow, past a month red — a month being also what
snap --old reports on, so a red count means "this machine is in that report".
A machine with none shows a dash. The count is what the column says and the age
is only how it is said, which is why the sheet spells the date out: a colour
cannot be read in a pipe.
Two columns are not always there, because they hold an exception rather than a property, and thirteen characters of blank down two hundred rows is thirteen characters spent on nothing:
- TASK appears while vCenter is doing something to any machine in the list — a clone, a migration, a consolidation, with its progress — and is gone again when nothing is going on. A column that turns up because somebody started a clone is not the layout shifting about: it is the news.
- WHY takes the guest operating system's place in the issues list (
^w,--issues), where every row has a reason to be there — and the four figures go with it, so that the reason has the width. It is the last column that listing gives up rather than the first: in a list whose every row is there because of it, dropping the reason first leaves a list of machines with no reason showing on any of them.
A terminal too narrow for all of that gives columns up, least useful first: the
guest's operating system, then the host, then the snapshot count, then the
address, then the vCPU count — so what survives longest is what a glance is for.
Each step only ever takes a column away, never brings one back, so dragging a
window narrower does not rearrange the table. An eighty-column terminal keeps
everything but the operating system, the host and the snapshot count: the name
column's minimum is one notch narrower than it reads in order to buy the address
its place there, and the count is the one column here that has somewhere else to
be said — ^w, --issues and snap --old all name it and date it.
The estate
^e answers the question the machine list cannot: where is there still room.
Every host of every server that answered, grouped under its cluster, with what
it carries set against what it has:
Estate v308, v309 14 hosts · 212 machines
CLUSTER / HOST ON/VM vCPU x MEM ALLOC x CPU LOAD % MEM USED %
v308 · prod 58/61 196/96 2.0x 1.1TB/768.0GB 1.5x
▸ esx01 21/22 72/32 2.2x 384.0GB/256.0GB 1.5x ████······ 41 ███████··· 72
esx02 19/20 68/32 2.1x 360.0GB/256.0GB 1.4x ███······· 32 ██████···· 64
esx03 18/19 56/32 1.7x 376.0GB/256.0GB 1.4x █████····· 53 ████████·· 81
v308 · standalone 4/4 8/16 32.0GB/128.0GB ██········ 12 ██········ 21
Two kinds of number, and the difference between them is the whole point — which is also what decides what a narrow terminal gives up. Three layouts, each the widest that fits whole: everything with the load drawn as bars; the same figures with the bars down to their percentages; and, narrower still, only the two ratios and the two percentages. Both kinds of number survive all three, because one of them alone says nothing.
What is allocated — every vCPU and every megabyte the machines on a host have been promised, added up. It routinely exceeds the host and is meant to: the ratio is the figure worth having, because 1.5x of memory is a decision somebody made and 8.0x is one somebody forgot. It is left blank where a host has room to spare, since a column of "0.4x" down a screen of healthy hosts is noise where the point is to spot the one that is over.
What is in use — what the host itself reports it is doing, as a bar and a percentage. A host at 2.2x allocation and 41 % load is fine; the same host at 90 % is not, and no allocation figure can tell those two apart. A load that is not known draws nothing rather than an empty trough: a host at one per cent fills none of the bar either, and "almost idle" must not look like "I cannot see this host".
A host vCenter has lost touch with keeps its hardware figures — they do not depend on reaching it — but reports no load at all rather than the nought its cached statistics would give: an empty bar is what an idle host looks like.
Only running machines are charged to a host. A machine that is switched off has been promised nothing it is using, and counting it would make a host of parked machines look full when the whole point of parking them there was that it is not — they are still in the ON/VM count, which is where that belongs.
⏎ on a host is the other half: it goes back to the machine list with the filter
set to that host, so the answer to "what is on this one" is the table everybody
already knows how to read, and Esc undoes it. The count it reports is the
host's own, not the filtered list's: the filter is a text match over the whole
row and carries no server, so it can also catch another vCenter's host of the
same name or a machine named after a host, and a count taken from it would then
contradict the screen it came from. ^r reads the screen again —
live mode deliberately does not tick here, because this screen reads the hosts
itself and a timer doing that every ten seconds would be paying for a screen
somebody is reading rather than watching.
The allocations are added up from the rows the list already holds, so no machine
is read twice for this, and they are matched to hosts by reference rather than by
name — the same lesson gvm host carries a comment about, where a host added by
address and renamed later reported zero machines while running dozens.
Live
^l and the list stops being a snapshot. It re-reads itself every ten seconds —
every two while vCenter is doing anything at all, so a clone's progress is
something one watches finish rather than a figure one waits for — and the title
says so, because a screen that moves on its own with nothing to explain it reads
as a fault.
It refreshes over the connections that are already open. ^r logs in again,
which is how a session that has died is recovered; doing that every ten seconds
would be three logins a minute for nothing.
^l is not the only way in: making a machine from a template turns it on, since
a clone that has just been started is exactly the screen this is for, and says
so in the same line that reports the machine.
Two things come with it.
The line under the table says what just changed. A table shows what is; this is the only thing on the screen that says what became:
db01 off · web01 +1 snapshot · app07 clone · esx03: disks need consolidating
Power states, snapshots appearing and going, tasks starting and finishing, machines arriving and leaving, and vCenter starting to complain. Four of them and a count of the rest, because a line that has to be read carefully is a line nobody reads. It is the reason to leave the thing open.
Where two vCenters hold a machine of the same name the server goes in front of it — "v309 web01 off" — and only there: a name is not what makes a machine that machine, and the width is worth spending on the rare case rather than on every line. A quiet refresh clears that line and nothing else: a message somebody produced by pressing a key is theirs to keep.
The CPU~ column is where each machine has just been. Six sweeps of its
processor load, one character each:
NAME PWR CPU CPU% CPU~ MEM
app07 on 4 18 ▃▅█▆▄▂ 8.0GB
web01 on 4 82 ▄▂▁▃▅█ 8.0GB
The scale is fixed at 0 to 100 and never fitted to the samples: a line that scales itself would make a machine idling between 1 and 2 per cent look exactly like one swinging between 40 and 80. The column is there only once there is something in it — a machine has a history after its second sweep — and it is the first thing after the guest's operating system that a narrow terminal gives up: everything else in the table is a fact about a machine, and this is a shape.
A machine that is not running has no load rather than a load of zero, so nothing is sampled from it and it draws nothing — and what it had drawn before goes with it, rather than leaving a busy shape beside a CPU% of "-".
The refresh holds still for every screen that asks something: a menu decides what it offers from the state it was drawn with, the picker holds a list being chosen from, and a half-typed name or a confirmation is an answer in progress. Being ten seconds out of date is better than any of those moving under a hand. A machine's sheet is refreshed, at the line it was being read at — watching one machine's memory is a reason to have it open.
Nothing here acts on a machine. A refresh that could start or stop something would be a timer with the power to do it, and that is the one thing a screen left open unattended must not have.
Sorting
^o puts a legend on the status line and the next key picks the order, so the
list stays on screen while it rearranges itself:
sort: n·name p·pwr c·cpu% m·mem% z·snaps o·old w·why s·size u·cpus v·vc h·host a·ip r·reverse
Each order comes with its own direction, because that is what asking for it
means: by name is a to z, by processor load is the busiest first. r reverses
whatever is current. Anything that is not a choice — Esc, a stray letter — leaves
the table as it was.
The title says which order the table is in (↓ cpu load) and the heading of that
column is lit in the same colour, so the state is visible without asking. A
missing value is never a small one: a stopped machine has no load and a machine
whose guest is silent has no address, and both sort to the bottom whichever
direction the order runs. Machines that compare equal stay in name order, so
flipping the direction on a screen full of identical figures does not reshuffle
them.
Thirteen choices are ninety-three columns, so a terminal narrower than that gets them on two lines instead of one that runs off the right-hand edge, hiding the very choices the legend exists to offer. They break where the meaning breaks — what the machine is doing and what it wants doing to it, then what it is made of and where it lives — and the second line takes the help line's row, which describes keys that do nothing while a menu is waiting for one. Decided at render time, so a window dragged wider gets the one line back:
sort: n·name p·pwr c·cpu% m·mem% z·snaps o·old w·why
s·size u·cpus v·vc h·host a·ip r·reverse
Three of them are about the two columns that are new:
zis by how many snapshots the machine is carrying, most first. Nought is a figure here and not a missing one — nothing to clean up is a fact about the machine — so a machine with none sorts where nought belongs: at the bottom going down, at the top coming back up. The letter carries no mnemonic because every letter that does was taken;--sort snapsspells it out.ois by the age of the machine's oldest snapshot, oldest first, which is the order the housekeeping is done in — a different question from#, and the more useful one: one snapshot from March wants attention before six from this morning. A machine with no snapshots has no age and sorts to the bottom either way round, the same as a stopped machine's load does.wis by what is wrong with the machine: broken above wants-a-look above nothing to report, and within each the machine with the most to answer for first. Sorting the reasons as text would put "alarm" above "disks need consolidating" and mean nothing at all. Run the other way up it is the machines that are fine, by name — a listing worth having too.
The order survives ^r, and the selection follows the machine it was on. gvm vm -l --sort cpu% --reverse takes the same orders by letter or by name.
A vCenter that does not answer is named in the title in red — on v308, v108 v38 unreachable — for as long as the list is open, and the reason is on the
status line when it opens. Only the servers whose machines are actually there are
named as holding them.
The machines that want looking at
A list of two hundred machines is read by running the eye down it, which is
exactly the wrong way to find the three that are broken. ^w narrows it to
those, and each one carries the reason in place of its guest operating system:
NAME VC PWR SNAP IP HOST WHY
old01 v108 on 1 10.0.0.31 esx02 /var 97 % full · no VMware Tools
db01 v308 on 3 10.0.0.12 esx01 disks need consolidating · snapshot base is 63 days old
win7 v38 on - - esx07 vCenter says yellow
The four figures — vCPUs, processor load, memory and memory in use — are not there. A machine is in this list because something is wrong with it, and how hard its processors happen to be working at this second says nothing about any of the reasons: they would be four columns of arithmetic between the machine's name and the answer to the question that was asked. They are one keystroke away in the ordinary list, and on the machine's own sheet.
Nothing new is asked of the servers: this is a filter over the sweep that is
already on screen, so it costs a keystroke and no waiting. ^w again gives the
whole list back, the typed filter still applies inside it — ^w web is the
broken web servers — and the title says issues only for as long as it is on,
because a filtered list that does not say so is a lie told by omission. ^o w
puts the worst of them at the top.
What counts as an issue is deliberately narrow, because a list that cries wolf is one nobody opens:
| reason | |
|---|---|
| disconnected, orphaned, inaccessible | vCenter cannot see the machine properly |
| waiting for an answer in vCenter | a question nobody has answered; the machine is stopped until somebody does |
| disks need consolidating | deltas left behind by a snapshot removal that did not finish, growing quietly |
| alarm: name | what vCenter itself is complaining about, by the name somebody gave the alarm |
| vCenter says red / yellow | the rolled-up status, when no alarm came with it to explain it |
| no VMware Tools | and only while the machine is running |
| /var 97 % full | a guest filesystem past 90 %, named with the figure |
| snapshot name is 63 days old | past a month, which is where the table's red begins |
Broken is red and wants-a-look is yellow, worst first — which matters because the column is truncated from the right. An alarm somebody has acknowledged is one a person has dealt with already and is not reported; a machine that is switched off is not a fault; and the things that are only true of a running machine are not held against a stopped one.
The same list prints: gvm vm -l --issues, which is the morning's glance and
the one worth a cron job.
Nothing acts on a machine from the table. Everything that changes one lives in
the machine's own sheet, which ⏎ opens — a row of a table of two hundred
machines is something the eye runs past, not something anyone has read. In the
sheet:
^a the action menu (see below)
^s take a snapshot: a name, then a confirmation
e recent events
h ssh to the guest, as root
y copy that name or address to the clipboard
w open in the vSphere client
Pressing ^a or ^s in the table says so rather than doing nothing visible.
^s takes two steps.
First the name. The field starts empty and its hint says which random name Enter
alone would use — the same kind snap -n gives — so ^s enter y is the quick
path and typing something that will still mean something in three weeks is the
deliberate one. ← → Home End Backspace Delete edit it, Esc abandons the whole
thing, and 80 characters is where vSphere stops.
Then the question, naming the snapshot, the machine and the vCenter. It takes
nothing but y: Enter finishes a name, it never takes a snapshot. Afterwards the
name is on the status line, and a sheet that is open jumps to its snapshot
section so the new one is there to see.
Snapshots are taken without memory and without quiescing, and both are deliberate. Memory would keep the running machine's RAM too, so that a rollback came back mid-flight — at the price of writing all of it to the datastore every time, and of a rollback that restores a process tree along with the disks. What these are for is the moment before a patch, where coming back to a machine that boots is the point. Leaving quiescing off makes the disk state crash-consistent — what a machine finds after the plug is pulled, which a journalling filesystem handles and a database may not — and means a snapshot neither depends on VMware Tools running nor stops when they are not.
Making a machine from a template
p in a template's action menu, gvm new on the command line. It is the one
thing gvm does that brings a machine into being rather than acting on one that
already exists, and that makes its hard question where rather than whether.
A template is not a machine that happens to be switched off. vSphere takes its resource pool away when it is marked as one, so a copy of it has nowhere to run until somebody says where — and that is the one thing that cannot be inherited from the source. Everything else is: the folder it sits in, the datastore it lives on, the hardware it was built with.
So the placement is worked out first and the question says it in full:
make web05 from ubuntu-24.04-template on v308? y = yes, anything else = no
it will run in prod-cluster, powered off
The resource pool is the one the template's own host belongs to — the cluster's,
where it is in a cluster, which leaves the choice of host to DRS the way every
other deployment there does. --host overrides that and pins it; --datastore
puts it somewhere other than the template's own.
It does not wait. A clone copies every disk the template has, which is
minutes to the half hour, and a list frozen for that long is a list nobody would
start one from. The task hangs off the template in vCenter, so the row it was
started from shows how far along it is in its TASK column. gvm new on the
command line does wait: a script that gets its prompt back wants the machine to
be there.
And it goes to that column. The machine's sheet — which is where the menu
was opened and a page of facts about the template — is put away, the list
comes back with the cursor still on the row the clone hangs off, and live mode
turns itself on if it was off, so the percentage moves on its own and the line
under the table announces the new machine when it exists. ^l ends it again.
Naming the key that would make an invisible figure move is three steps of
housekeeping after the one decision that mattered.
The rest of a template's menu is greyed out with "a template" next to it, because vSphere will not start one, snapshot one or reconfigure one. They are greyed rather than left out: a menu that changes shape between rows is one nobody learns.
Telling the guest what it is
A copy of a template comes up as the template: same hostname, same address. Those two are the facts that are about this machine, and everything else — the domain, the resolvers, the netmask, the gateway, the timezone — is the site's answer, the same for every machine. So the two are typed and the rest is kept somewhere it is written once.
Two places it can be kept, and gvm takes it from either.
In ~/.gvmrc, which is the short road for a site that keeps no specifications
in its vCenter:
domain = fhi-berlin.mpg.de
dns = 141.14.128.1, 141.14.128.2
netmask = 255.255.255.0
gateway = 10.0.0.1
timezone = Europe/Berlin
gvm new --from ubuntu-tpl --name web05 --ip 10.0.0.55
gvm writes the customisation itself from those: hostname from the machine's name
(--hostname where they differ), address from --ip, and the five above. It is
Linux only — a Windows guest is a Sysprep, which is a licence key, an
administrator password and a domain to join, and none of that belongs in a
configuration file next to the SMTP relay. gvm config shows what it would
write, and names the settings that are still missing.
In the vCenter, as a customisation specification made in the vSphere client under Policies and Profiles:
gvm new --from ubuntu-tpl --name web05 --spec linux-static --ip 10.0.0.55
--spec names one, and gvm overrides exactly two things in it — the
hostname and the address — and leaves the rest alone. This is the road for
Windows, and the one to prefer where the specifications already exist: the
policy is then where the web client and every other tool can see it too. A tool
that knew better than the vCenter about any of the rest is how one ends up with
a network policy nobody remembers agreeing to.
In the list it is a step of its own: after the name, a picker of whichever roads are open —
customise web05 how v308
▸ leave the guest as the template made it
this site — fhi-berlin.mpg.de, gateway 10.0.0.1
linux-static
windows-domain
— and then one line for the address, which says in words what leaving it empty would do:
address for web05, or empty for DHCP:
address for web05, or empty to leave it to linux-static:
Where that picker does not appear, the confirmation says why rather than
leaving a step to look broken — neither road is open, because the vCenter holds
no specifications (or will not say) and ~/.gvmrc has not been told the site:
make web05 from ubuntu-tpl on v308? y = yes, anything else = no
no specifications on v308, and ~/.gvmrc has no domain, netmask, gateway · in prod-cluster, powered off
gvm new --specs lists what a server holds, and prints the server's own words
when it will not say.
An address it fetches rather than one you look up
gvm new --from ubuntu-tpl --name web05 --ip auto
Where the site has a tool that hands addresses out, --ip auto asks it instead
of making you look one up first. In the list the same word does it:
address for web05, "auto" for one from dns, empty for DHCP:
It shells out to dns, the Infoblox helper — found on the path by that name, or
named as dnstool in ~/.gvmrc where it lives somewhere else. Everything it
knows (the appliance, the credentials, the default domain) stays its own; a
second copy of any of that inside gvm would be a second thing to keep right.
gvm config says whether one was found, because "why was I not offered that"
is the question that follows an option quietly not being there.
The name has to be free. It is asked before anything is added, and a name
the appliance already knows is refused rather than added to — what dns -a
would do with one is either refuse in worse words, or hang a second address on
somebody else's host record:
web05 already exists (web05.fhi.mpg.de at 141.14.140.182) — give the machine
another name, or free that one with 'dns -d web05'
Which is the same question gvm asks the vCenter about the machine's name, one answer short of the same answer.
And the address is said where it stays. In the confirmation, and again in the line that is left on the screen afterwards — this is a number somebody writes down:
web05 is being made at 141.14.140.182 — live on, ubuntu-tpl shows how far
along it is
Three more things about it are deliberate:
-
It is asked with
-j, and nothing older than v2.5.0 is asked at all. From that version the helper answers in an envelope:ok, the reason in anerrorfield of its own, the record — and an exit status that agrees with it.{"ok":true, "action":"showhost","name":"v308.fhi.mpg.de","record":{...}} {"ok":false,"action":"showhost","error":"host '...' not found"}What came before said the same things in a sentence and exited 0 while refusing, which is what an earlier version of this got wrong by believing exit statuses. Rather than keep reading both, gvm asks the helper its version once and uses it only from 2.5.0 up. An older one is not silently ignored — "there is none" and "the one you have is too old" send somebody to two different places, so it says which:
/usr/local/bin/dns is 2.4.4, and gvm wants 2.5.0 or newer — 'dns --update' fetches itgvm configsays the same thing when nothing is being deployed.An appliance that cannot be reached is an error and not a free name: concluding "nobody has this name" from a server that is down is the last thing to do before asking it for an address.
-
The record is read back, not taken from the sentence the helper prints.
OK: host 'web05.fhi.mpg.de' added with IP '141.14.140.182'is written for a person and gets reworded between versions; the record is the appliance's own answer to the same question, and where the two disagree the record is what the machine will have. -
It is fetched before the confirmation, so that the question says the address rather than a promise of one:
make web05 from ubuntu-tpl on v308? y = yes, anything else = no in prod, powered off · named and addressed for fhi.mpg.de at 141.14.140.182 (web05.fhi.mpg.de, from dns) -
And given back where it is not used. A deployment abandoned at that question, or one the vCenter then refuses, releases the address rather than leaving a record behind for a machine that was never made.
The name asked for is the machine's own — the short one, since the helper puts the record in the site's default domain itself — which is also the hostname the guest is given, so the two cannot disagree.
Three things it refuses rather than guesses at, on either road:
- An address with no netmask to go with it. Where the specification's adapter takes its address from DHCP it carries no mask, and a machine given an address without one half works. Where to get a mask from is a question for the vCenter.
- A hand-written Windows answer file, and a cloud-init specification. Both carry the hostname inside a format gvm does not own, and reaching into either to change one line would be guessing.
- Nothing at all, silently. If the template reports no VMware Tools — which is what carries a customisation out inside the guest — it says so before the clone rather than leaving it to be discovered twenty minutes later on a machine that came up under the template's name. It is a guess and is said as one: the machine is made either way.
One more thing it says rather than refuses: a gateway that is not on the machine's own network. A routed setup can put one anywhere, so it is not an error — but almost every time it is a typo, and a machine that cannot reach its gateway is one somebody drives to the console for.
Deploying without either road still works, and the guest comes up as the template made it.
The sheet's four letters
The four letters are the things that change nothing, on the machine or on the vCenter: they read its history, copy its address, open it somewhere else. They are letters of the sheet rather than entries in the menu because nothing they do needs thinking about first, and — unlike the table, whose filter swallows every ordinary letter — the sheet has nothing else to do with them:
-
eputs the machine's own recent events at the foot of its sheet and scrolls down to them — why is this thing off, who rebooted it, what happened at four this morning.gvm logis the whole vCenter over the last hour, which is the right shape for a mail and the wrong one for that question. They are fetched when they are asked for: opening a machine stays one call. -
hlogs in to the guest as root —ssh root@<name>— by its own hostname where it reports one and by its address otherwise. The terminal goes back to what it was for as long as that lasts.ssh = ssh -l someone %hin~/.gvmrcreplaces that command whole, root and all; the target is always one argument and never goes through a shell, because it is a name the guest chose for itself.^Cwhile it hangs on a machine that is not answering kills the login and no more than that: gvm catches the signal for as long as the child has the screen — the terminal is in its ordinary mode there, where the keystroke goes to every process in the foreground group — and comes back to the sheet saying the login was interrupted. -
ycopies exactly whathwould connect to — the hostname where the guest reports one, the address otherwise — and the status line names which of the two it was and which clipboard it went into, because a clipboard is invisible and "copied" on its own is something one has to go and check.Two routes, because neither alone is enough.
pbcopy(orwl-copy,xclip,xsel) is the one that always works where there is one, and the terminal's own OSC 52 escape sequence is the only one that reaches the right machine from the far end of an ssh login — where a localpbcopywould copy into the clipboard of a machine nobody is sitting at. So the sequence is always sent and the command is used as well where there is one; a login is recognised bySSH_CONNECTION, and there the sequence is the whole story. It is also the route a terminal is free to ignore — iTerm2 keeps it behind Applications in terminal may access clipboard, tmux behindset-clipboard— which is why the line says when it was the only one used. -
wopens the machine's page in the vSphere client. The link needs the vCenter's instance UUID, which is the serverGuid that client puts in its URLs and the one thing gvm cannot work out from the configuration; where there is no browser to hand off to, the URL is said and copied instead.
h and y need somewhere to connect to: on a machine whose guest is not
reporting an address they say so on the status line rather than doing nothing.
The action menu
In a machine's sheet, ^a opens the menu for it. It is everything that changes a
machine, and it lives there and nowhere else — the list is arrowed through and its
filter swallows every ordinary letter, so a hotkey that powered a machine off
would sit one fumbled control key away from an outage, and the sheet has to be
opened first anyway.
Above the choices the menu repeats the few lines of the sheet the choice depends on — state, guest, hostname, address — taken from the sheet itself, so the two cannot word the same fact differently. On a terminal too short for both, those lines go one at a time, least useful first: the state stays longest because every choice depends on it, then the address and the hostname, which say which machine this is about.
n take a snapshot o power on
r revert to a snapshot ... s shut down the guest
d remove a snapshot ... b reboot the guest
D remove ALL snapshots S power off (hard)
──────────────────────────────── B reset (hard)
c change the vCPU count ...
m change the memory ...
Of the power pairs, lowercase asks the guest and uppercase acts at the hypervisor: the violent variant always needs the shift key. What cannot be done right now is greyed out with the reason next to it — "no VMware Tools", "already running", "no snapshots", "needs it off" — rather than left out, and picking it anyway spells the reason out instead of running it.
The three groups are what the machine has been, what it is, and what it is doing.
Snapshots are drawn as the tree they are — which state descends from which is the whole point of a snapshot list — and the one the machine is running from says so:
snapshots base (01.09.2026 02:00)
├─ after-patch (03.09.2026 09:12)
│ └─ hotfix (03.09.2026 16:40)
└─ before-boot (05.09.2026 07:00) ← current
The same drawing appears in the picker below and in gvm snap -l; there is one
function that draws it, so the three cannot drift apart.
r and d open a list of the machine's snapshots. What is chosen there is
carried on by its vSphere reference, not by its name: two snapshots of one
machine may share a name, and a rollback point is not something to identify by a
string that is not unique.
Then comes a full page: the machine, the vCenter, the datacenter, the host, the
state, and what the operation costs — and it asks for YES, in capitals.
Not y, not yes, not Enter. A word that needs the shift key cannot be given by
a hand resting on Enter, and in the list the machine is whatever the cursor
happens to be on, so the page above the prompt is the part that matters.
Powering on is the single exception, and ends at one y: it destroys nothing.
Reverting asks vCenter not to start the machine again afterwards, so a machine whose snapshot was taken while it ran does not come back up with a rewound disk while you are still reading the message. Afterwards gvm reads the power state back and reports what it actually is rather than what it asked for.
After an operation the machine's row is read again, so the list shows what
happened rather than what the last sweep found. For a power on or off that means
waiting for vCenter's own view to catch up first: the task finishes a moment
before the property collector agrees, and a row re-read in between would still
show the old state. Opening the menu re-reads too, which is what picks up a guest
that has finished shutting down in its own time — ^r reloads everything.
Nothing else here writes: no key changes a setting, and there is no way to delete a machine.
The sweep the list makes at the start brings back everything the table and the
sheet show, the snapshot trees and the running tasks included — they are
properties of a machine, and reading them for every machine is one call, not one
per row. Two things are asked for afterwards, for one machine at a time: its
snapshots when its sheet is opened, because everything that acts on a snapshot
addresses it by reference and a reference out of a sweep that ran minutes ago may
name one somebody has since removed; and its events, when e asks for them.
It needs a terminal, and says so before it connects to anything — in a pipe or
under cron, use gvm vm -l.
The dangerous half
snap --revert, snap --removeall, power --off and power --reset stop or
rewind a running machine, and the first of them destroys data outright. Four
things hold for all of them:
- Graceful and hard are separate commands.
power -sasks the guest's operating system to shut down;power --offcuts the power at the hypervisor. gvm never turns the first into the second because VMware Tools did not answer — it refuses, and names the hard variant so you choose it deliberately. - The destructive options have no short letter.
--revert,--removeall,--offand--resethave to be spelled out; only the harmless ones (-o,-s,-b,-l,-n) are one keystroke. - Nothing impossible is sent. A machine that is already off is not shut down
again, and a graceful operation on a machine without Tools is refused before
anything reaches vCenter.
-ydoes not override this. - They ask first, and fail closed. Each prints what will happen, to which
machine, on which vCenter, and asks — defaulting to no.
-yanswers in advance, which is what cron needs. Without a terminal and without-ythe command refuses with exit status 1 rather than doing nothing quietly: a script that gets "nothing done" and exit 0 would believe the machine was stopped.
One power operation per command line; two is a mistake, not a sequence, and gvm says so instead of guessing.
What a machine has
c and m in the menu, gvm size on the command line: the vCPU count and the
memory. Nothing is lost by either, so they get the plain y/n question rather than
the page that wants YES typed out — a number set wrongly is set back.
vSphere refuses most of what one might ask for on a running machine, and gvm refuses it first, with the reason, rather than sending it to be bounced:
- A running machine can only grow, and only where it was built to. CPU and memory hot-add are per-machine settings, turned on when the machine was made. Without them the entry says "needs it off". Memory can never shrink while a machine runs — there is no hot-remove for it in vSphere at all — and vCPUs only where hot-remove is on as well.
- The socket topology is the machine's own. A vCPU count has to be a multiple of the cores per socket, and gvm does not quietly change the sockets to make a number fit: software is licensed per socket, and a tool that turns 2 sockets into 4 to accept an odd number would be writing somebody an invoice. It names the two counts that do fit instead.
- Memory is whole multiples of 4 MB. It is typed in gigabytes, because that
is what the sheet shows and what anybody says out loud;
512mmeans megabytes, and1.5gis 1536 MB. - Read again immediately before it is sent. The menu's grey-out says only that a change of this kind is possible at all; between drawing it and answering the question somebody else may have started the machine.
- A machine whose configuration cannot be read is left alone. Not knowing what it has means there is nothing honest to put on the left of the arrow, so both entries grey out and say so rather than offering a change from a figure gvm does not have.
gvm size -c 8 -m 1026mdoes neither. Both numbers are read and checked before either is sent: a command line that sets the vCPUs and then refuses the memory has half happened, which is the one outcome nobody asked for.
gvm size --vm <machine> on its own changes nothing and says what there is:
machine web01 (running)
vCPU 4, in 1 per socket
memory 8.0GB
while it runs add vCPUs, add memory
That last line is the one worth having before planning the work — it is the difference between a change now and a maintenance window. Where a machine has none of the three settings it says so plainly: "nothing — it has to be powered off to be changed".
Hot-added resources are not necessarily in use the moment vCenter reports the task done: the message says as much, because an operating system does not always notice on its own that it has been given another four processors.
The printed listing (vm -l) is the same table: the same columns, the same
cells, the same colours, fitted to the terminal when there is one and written out
in full into a pipe, where the colours are left off.
The reports
Two things nothing in vCenter does for you, in the shape a cron job wants:
every server at once, one line per thing, and -m to put it in the post.
Old snapshots
gvm snap --old # older than 30 days, on every vCenter
gvm snap --old -d 7 # or than a week
gvm snap --old -m # and mail it
Somebody takes a snapshot before an upgrade, the upgrade goes well, and the snapshot stays. Six weeks later its delta disk is bigger than the machine and the datastore is the thing that pages you.
3 snapshots older than 30 days, on 2 machines:
MACHINE VC SNAPSHOT AGE TAKEN SIZE
old01 v108 base 208d 12.02.2026 03:00 8.9GB
db01 v308 before-patch 63d 06.07.2026 22:14 41.2GB
db01 v308 hotfix 61d 08.07.2026 09:40 2.1GB
52.2GB in 3 snapshots, on 2 machines
Oldest first, which is the order the work is done in and puts the worst line where a mail gets read. The age takes the table's colours — yellow past a week, red past a month — and the mail carries the same table with the colours left off, out of the same cells, so the two cannot come to different conclusions.
The size is what removing that snapshot would give back: its own state file and the last link of each of its disk chains. The links in front of those belong to its ancestors, and the delta the machine is writing to right now belongs to no snapshot at all — so summing whole chains, which is the obvious thing to do, reports the same delta once per descendant. A snapshot whose file layout could not be read shows a dash rather than 0 B: nought bytes and "not known" are different answers, and the second must not invite somebody to remove the wrong snapshot.
The file layout is only asked for for the machines that actually have snapshots, and for all of them at once per server — it lists every file of every machine, which is far too much to carry through the ordinary sweep.
Datastores
gvm ds # one line per datastore
gvm ds -t # and post the numbers
The gap next to gvm host: a cluster is watched by its processor load and its
memory, and then it falls over because a datastore filled up.
DATASTORE TYPE CAPACITY FREE USED% PROVISIONED OVER% VM STATUS STATE
ppb-ssd-1 VMFS 4.0TB 412GB 90 5.1TB 128 41 green ok
ppb-sata-2 VMFS 8.0TB 3.2TB 60 6.0TB 75 88 green ok
ppb-old VMFS - - - - - 3 red inaccessible
3 datastores 3.6TB of 12.0TB free (70 % used)
Provisioned is what has been promised out of the datastore: what is in use plus what thin disks are still entitled to grow into. Past the capacity that is a promise the datastore cannot keep if every machine takes what it was offered, which is why it has a column of its own rather than being folded into "used" — ordinary practice, so a hundred per cent is a word of warning in yellow and half again as much is an alarm in red.
Every figure comes out of the datastore's summary, and vSphere only vouches for those while the datastore is accessible: an unreachable one reports dashes rather than zeroes, because a datastore that says 0 B free looks like an emergency and one nobody can reach is a different one.
The listing as a document
gvm vm -l --json is the same sweep, for something other than a person:
{
"generated": "2026-09-08T11:42:07+02:00",
"answered": ["v308", "v108"],
"failed": ["v38: login failed: ..."],
"count": 212,
"machines": [
{
"name": "db01",
"vcenter": "v308",
"power": "poweredOn",
"cpu_percent": 12.4,
"memory_percent": 64.1,
"snapshots": [{"name": "before-patch", "days": 63, "current": true, ...}],
"oldest_snapshot_days": 63,
"task": {"what": "consolid", "progress": 40, ...},
"issues": ["disks need consolidating"],
...
}
]
}
Two things about the shape, because a document is a promise:
It is one object and not an array of machines, because a listing that quietly leaves out a vCenter which did not answer is worse than no listing at all — a script handed a bare array cannot tell an empty cluster from an unreachable one. The servers that answered and the ones that did not are in the document, and a failure is not also printed as prose: a line of English in the middle of the JSON would break whatever is reading it.
And a figure that is not known is null, never 0. A stopped machine has no
processor load and a machine whose guest is silent has no address; a spreadsheet
that averages a column of zeroes reports a fleet that is idle.
--json and --issues mean -l without having to be told twice, and both take
-m, --sort and --reverse like any other listing.
Shell completion
eval "$(gvm completion zsh)" # ~/.zshrc
gvm completion bash > /etc/bash_completion.d/gvm
Machine names are long and there are hundreds of them, which is what makes the
non-interactive half hard to type — gvm -v v308 snap -l dbse<tab>. The
completion offers them after the options that take a machine, the server names
after -v, and every subcommand and option otherwise.
That last half is not written down anywhere: flaggy generates it out of the
parser itself, so no list can fall behind the options that exist. gvm answers
the completion subcommand one step before flaggy would, keeps what flaggy
wrote, and adds the names on top — a wrapper that falls back to flaggy's own
function by the name it installed it under, read off the script rather than
written down a second time. fish, powershell and nushell are left to
flaggy entirely; the names are wired up for zsh and bash.
The names cannot come from the vCenters: a completion runs on every Tab and has
to answer in milliseconds, and three logins take seconds. So they come out of
what gvm last saw — every sweep of the machine list leaves them in the cache
directory, per server and with the time on them, and --complete-vms reads that
file and nothing else. A sweep of one server leaves the others' names where they
were, so completion keeps working for a vCenter that is down.
Neither the subcommand nor those two options read ~/.gvmrc: a Tab key must not
rewrite a file, and reading the configuration seals any password standing in it
in the clear.
Nothing else in gvm reads that cache. Every command resolves the name it was
given against the server itself, because "what gvm saw last time somebody
looked" is the right currency for a Tab key and no currency at all for anything
that acts on a machine. gvm config says how old it is, so that a completion
offering a machine deleted last month can be explained.
Colours
The palette is mwxcol, copied into
colors.go — that repo's mwxcol.go is the source of truth, so a change there
is a change here. Everything gvm paints goes through that one file: the P/PF
helpers in tools.go through the C* functions, the full-screen list through
the escape sequences at the bottom of it.
The frame follows the mapping mwxcol's own fzf theme uses, job for job: the
selected row on a darker surface with a violet pointer, the filter's hits in
pink, counts in green, questions in yellow, errors in red, headers and
the help line in dark.
Every line at the foot of the screen that wants an answer is that one yellow,
whatever kind of question it is: the sort legend, both its lines; a yes/no
question and its hint; the label in front of a snapshot name or the YES of a
confirmation. They are different kinds of question and one state — gvm is
waiting for a key — and that state is worth learning once, in one place and one
tone, rather than being worked out per screen. What is typed in answer stays
white: it is the operator's, not part of the question.
Inside the table and the sheet every colour is a role, not a decoration:
white |
the machine's own name, and its guest |
violet |
which vCenter — a kind of thing |
green / dark / yellow / red |
powered on / off / suspended / anything else |
blue |
addresses, paths and dates |
orange |
sizes and counts |
pink |
names a person gave: snapshots |
grey |
present but seldom read: host, guest os, uuids, an event's history |
yellow / red |
a load past 75 / 90 %, a snapshot past a week / a month, something that wants a look / something broken |
yellow |
what is being done to a machine right now: the task column |
A machine that is off is dark rather than red — being switched off is not a
fault. Two places lift that tone to grey: the selected row, where dark would
sit on the darker surface, and every value on the sheet, where the labels
beside it are dark themselves. Lifted, it is still visibly quieter than the
rest of the palette, so what was dimmed stays dimmed.
Colours are written as true colour (24 bit). In a pipe the C* functions leave
them out; the full-screen list needs a terminal anyway.
Updating
gvm updates itself from the releases of the Gitea instance named at the top of
selfupdate.go:
gvm --check-update # look, change nothing
gvm --update # fetch and replace the running binary
gvm --version
Once a day an ordinary run looks in the background and, when there is something
newer, prints one line about it. It costs nothing in the foreground, says
nothing when gvm is not on a terminal, and GVM_NO_UPDATE_CHECK=1 turns it off.
The downloaded binary is run once with --version before it replaces the
running one, so a truncated or wrong-platform download cannot install itself.
Building
./build.sh # all platforms into ./bin
PLATFORMS="linux/amd64" ./build.sh
VERSION=1.1.0 ./build.sh # set the version instead of bumping it
go test ./... # incl. tests against govmomi's simulator
Every run bumps the patch level in version.txt and injects it into the
binaries. The files in ./bin are named the way --update expects them in a
release: gvm-<goos>-<goarch> on a release tagged with the bare version number.
The automatic bump only ever touches the last number, so VERSION= is how a
major or minor step is made — no number of builds reaches 1.0.0 from 0.x. It is
checked to be MAJOR.MINOR.PATCH before anything is built: that number ends up
in the binary, in version.txt and on the release tag, and --update compares
versions number by number, so anything else would compare as older than
everything and quietly stop updates.
Tests
go test ./... runs without touching any real vCenter. The list, the filter,
the parameter sheet and the palette are checked on synthetic machines, and
everything that talks to a server — logging in, the inventory sweep, the
snapshot round trip, ^s with its name field and its confirmation, the whole
power and revert half with its confirmations declined and then given, the error
paths — runs
against govmomi's own simulator, started inside the test process (see
sim_test.go). The configured vCenters are production; no test goes near them,
and none of them reads ~/.gvmrc.
The checks that stand between a keystroke and a machine are tested on their own as well: the whole power matrix (state × VMware Tools × operation), that a refused operation sends nothing, that an unavailable menu entry does not run when it is picked anyway, that only the exact machine name passes the confirmation, and that removing one of two identically named snapshots removes the one that was picked.
So are the judgements the reports are made of, which are the ones that would go wrong quietly:
- every reason a machine can be in the issues list, one by one, and the ones that must not put it there — a stopped machine, an acknowledged alarm, a filesystem with room, this morning's snapshot, a status reported twice
- that a snapshot's size counts each delta once and not once per descendant, which is what summing whole disk chains does
- that an unknown figure is a dash on screen and
nullin the document, for the load, the address, the uptime, a datastore that cannot be reached and a snapshot whose file layout could not be read - that the ssh target is one argument and never shell code — it is a name the guest chose for itself
- that
ynames what it copied and that an ssh login uses no local clipboard tool — the tests say they are a login, which also keeps them off the clipboard of whoever is running them - that a step which does not happen says why — a vCenter with no customisation specifications, and one that will not let them be read, both used to skip the question in silence — and that the reason and the placement both fit on the line they share
- that a name already in the appliance is refused before anything is added to it, that an appliance which cannot be reached is not mistaken for a free name, and that a helper older than 2.5.0 is refused by name and version rather than talked to — against a stand-in, since a suite that called the real one would be editing the institute's network every time it ran
- that the version is read off the tool's own, and not off the toolbox version in the same line — which is the wrong answer that check exists to prevent
- that an address fetched from the site's tool is read back from the record rather than from its printed sentence, that the tool's own words come back when it refuses, that a fetched address is given back when the deployment does not happen, and that nothing is ever asked of it for an address that was typed — against a stand-in, because a test suite that called the real one would be editing the institute's network every time it ran
- that the specification gvm writes itself carries the site's five settings and this machine's two, invents nothing where an address was not given, and is refused for a Windows template and for a configuration that is not complete — named field by field
- that a customisation writes the hostname and the address into a vCenter's own specification and changes nothing else in it, for Linux and for Windows; that an address with no netmask behind it is refused, as are the two specification kinds gvm will not reach into; and — against the simulator's four real specifications — that the whole way through works
- that a deployment is refused from anything that is not a template, that a name already in use is refused before the clone starts, and — against the simulator — that what comes out is a machine rather than another template, in the pool it was given, switched off
- every rule a resize is held to: what a running machine may and may not be given, that the cores per socket are never changed to make a vCPU count fit, that a refusal never names 0 vCPUs as a count that would, and — against the simulator — that a reconfigure actually lands, read back from the server rather than assumed, and that a command line asking for one possible change and one impossible one makes neither
- that a
^Cduring a login does not take gvm with it, and that the login still dies of it: the signal is caught for as long as the child has the screen, and catching is not ignoring — an ignored one would be inherited by the ssh - that a refresh which half failed still leaves the rows and the view describing the same list, and that the screen draws — it panicked before
- that every layout of the estate screen fits the terminal it was drawn for, at nine widths from 60 columns up
- that a page step on that screen does not leap twice when it lands on a cluster name, and that an unreachable host draws no load
- that the estate screen groups the hosts under their clusters, that a heading is the sum of what is under it, that every placed machine is charged to exactly one host, and that Enter comes back with the list filtered to it
- that a ratio says nothing where there is room to spare, and that an unknown load does not draw the same bar as an idle one
- that the live refresh reads over the session it already has and not a new login — with the session closed it fails rather than reconnecting — that it notices a machine stopped behind gvm's back and says so, and that the trend keeps its scale, is bounded, and is forgotten when a machine goes
- that the column ladder still only ever drops columns with the task column in the table, and that a terminal of eighty still keeps the address
- that the completion cache survives a sweep of one server, forgets a machine
the server has forgotten, is written 0600, and that every option the
completion scripts complete after is an option
gvm.goactually declares - that the mailed report carries no escape sequences, and that
--jsonstays readable when a vCenter does not answer
gvm --version, the help and the options answered before the flag parser are
checked against each other too: anything the help promises has to be something
gvm answers, and the two options the completion scripts call are deliberately
not in the help.