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.
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 |
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
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) ^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.
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.
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% s·size u·cpus v·vc h·host a·ip o·old 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.
o is by the age of the machine's oldest snapshot, oldest first, which is the
order the housekeeping is done in; 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.
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 CPU% MEM% WHY
db01 v308 on 3 10.0.0.12 12 64 disks need consolidating
old01 v108 on 1 10.0.0.31 2 18 /var 97 % full · no VMware Tools
win7 v38 on - - 0 9 vCenter says yellow
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.
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
Pressing either 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.
The action menu
In a machine's sheet, ^a opens the menu for it. Above the choices it 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, because two of the choices are about them.
Everything that changes a
machine 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.
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)
────────────────────────────────────────────────────────────
e recent events h ssh to the guest
y copy the address w open in the vSphere client
Lowercase asks the guest, 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 address" — rather than left out, and picking it anyway spells the reason out instead of running it.
The group under the rule changes nothing, on the machine or on the vCenter. It is here rather than on four more control keys of its own because this is where one already looks for "what can I do with this machine", and because the sheet's help line is not the place to learn 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, 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 root %hin~/.gvmrcsays how; the target is always one argument and never goes through a shell, because it is a name the guest chose for itself.ycopies that address to the clipboard — the terminal's own, asked for with an escape sequence rather than throughpbcopy, so it works over ssh and lands where the person actually is.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.
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.
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.
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 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.