VMware ESXi console: viewing all VMs, suspending and waking them up: part 4
Posted by jpluimers on 2021/04/29
Yesterday we ended with an overview of available and unavailable vim-cmd vmsvc
commands and the promise to try running the various power commands on all relevant VMs.
Let’s start with a summary of the commands, so it will be easier to make a list of scripts to run them on relevant VMs.
Available commands
vim-cmd vmsvc/power.getstate vmid
vim-cmd vmsvc/power.hibernate vmid
vim-cmd vmsvc/power.off vmid
vim-cmd vmsvc/power.on vmid
vim-cmd vmsvc/power.reboot vmid
vim-cmd vmsvc/power.reset vmid
vim-cmd vmsvc/power.shutdown vmid
vim-cmd vmsvc/power.suspend vmid
vim-cmd vmsvc/power.suspendResume vmid
Unavailable commands
vim-cmd vmsvc/power.startup vmid
vim-cmd vmsvc/power.resume vmid
vim-cmd vmsvc/power.wakeup vmid
List the vmid
values, power status and name of all VMs
Getting the vmid
Yesterday I showed a small statement that gives the list of vmid
values on an ESXi system:
vim-cmd vmsvc/getallvms | sed -n -E -e "s/^([[:digit:]]+)s+((S.+S)?)s+([S+])s+(.+.vmx)s+(S+)s+(vmx-[[:digit:]]+)s*?((S.+)?)$/1/p"
What I ideally want is not just the vmid
and name
for each VM from vim-cmd vmsvc/getallvms
, but also get the power state information from vim-cmd vmsvc/power.getstate vmid
.
For that, we need to parse the output of vim-cmd vmsvc/power.getstate vmid
, which can be three outputs:
Retrieved runtime info
Powered offRetrieved runtime info
Powered onRetrieved runtime info
Suspended
So basically it involves deleting the first line which was covered in part 2 of this installment, for example on my system:
# vim-cmd vmsvc/power.getstate 10 | sed '1d' Powered on
Getting VM name
Extracting both vmid
and name
from vim-cmd vmsvc/getallvms
at the same time is not easy, heck even impossible, so I decided to go the vim-cmd vmsvc/get.config vmid
way.
Getting multiple values out of some output is already very hard in
bash
, where usually the less difficult way is to use arrays. Since Busybox has anash
shell (see Busybox sh (actually ash derivative dash): checking exit codes), andash
does not do arrays, that route is gone.To give you an idea how hard this is in
bash
and how to sort of workaround the lack of array support inash
:
- [Wayback] shell – How to assign values to multiple variables in command line with bars – Unix & Linux Stack Exchange (explaining how to do this with multiple assignments)
- [Wayback] bash – Extract two values from the output of a command – Unix & Linux Stack Exchange (explaining how to use
tee
to pass output to multiple distinct commands)- [Wayback] linux – Extract multiple values using sed – Stack Overflow (how to use
sed
to split output over multiple lines)- [Wayback] linux – how to get elements from list in bash? – Super User (on the
bash
array syntax)- [Wayback] linux – reading a tuple from a file with awk – Stack Overflow (on how to use multiple arrays in
bash
)- [Wayback] shell – How to port to bash-style arrays to ash? – Unix & Linux Stack Exchange (explaining that unlike
bash
,ash
has no arrays)
This partial vim-cmd vmsvc/get.config vmid
sample output on one of my VMs that shows how to use head -n 31
to get just the first 31 lines of output:
# vim-cmd vmsvc/get.config 10 | head -n 31 Configuration: (vim.vm.ConfigInfo) { changeVersion = "2021-04-07T22:08:30.548274Z", modified = "1970-01-01T00:00:00Z", name = "X9SRI-3F-W10P-EN-MEDIA", guestFullName = "Microsoft Windows 10 (64-bit)", version = "vmx-14", uuid = "564d51ac-f6cf-e40b-b686-2f53a28a4bea", createDate = "2019-05-17T21:37:11.408173Z", instanceUuid = "52403d0e-7ccd-48da-bb21-7e966defccf7", npivNodeWorldWideName = , npivPortWorldWideName = , npivWorldWideNameType = , npivDesiredNodeWwns = , npivDesiredPortWwns = , npivTemporaryDisabled = true, npivOnNonRdmDisks = , locationId = "564d6b18-ecd1-2261-0127-146b3f3bc636", template = false, guestId = "windows9_64Guest", alternateGuestName = "", annotation = "", files = (vim.vm.FileInfo) { vmPathName = "[EVO860_500GB] VM/X9SRI-3F-W10P-EN-MEDIA/X9SRI-3F-W10P-EN-MEDIA.vmx", snapshotDirectory = "[EVO860_500GB] VM/X9SRI-3F-W10P-EN-MEDIA", suspendDirectory = "[EVO860_500GB] VM/X9SRI-3F-W10P-EN-MEDIA", logDirectory = "[EVO860_500GB] VM/X9SRI-3F-W10P-EN-MEDIA", ftMetadataDirectory = }, tools = (vim.vm.ToolsConfigInfo) {
The reason to go the vim-cmd vmsvc/get.config vmid
way is that it contains all the configuration info in a kind of JSON format (except the first two lines) and should be relatively easy to parse. Or so at least I hoped.
Basically I am interested in the value of name = "X9SRI-3F-W10P-EN-MEDIA",
however, there are multiple name
fields in the total configuration:
# vim-cmd vmsvc/get.config 10 | sed -n -E '/name =/p' name = "X9SRI-3F-W10P-EN-MEDIA", name = "EVO860_500GB",
So what I really want is the value of name = "X9SRI-3F-W10P-EN-MEDIA",
in between the (vim.vm.ConfigInfo) {
and files = (vim.vm.FileInfo) {
parts.
This can be done using sed
as it allows to specify a range using a start and end value using addresses
:
- [Wayback] sed: Addresses in sed
An address is either a decimal number that counts input lines cumulatively across files, a '$' character that addresses the last line of input, or a context address (which consists of a BRE, as described in Regular Expressions in sed , preceded and followed by a delimiter, usually a slash).
An editing command with no addresses shall select every pattern space.
An editing command with one address shall select each pattern space that matches the address.
An editing command with two addresses shall select the inclusive range from the first pattern space that matches the first address through the next pattern space that matches the second. (If the second address is a number less than or equal to the line number first selected, only one line shall be selected.) Starting at the first line following the selected range, sed shall look again for the first address. Thereafter, the process shall be repeated. Omitting either or both of the address components in the following form produces undefined results:
[address[,address]]
- Range Addresses (sed, a stream editor)[Wayback] Range Addresses (sed, a stream editor)
An address range can be specified by specifying two addresses separated by a comma (
,
). An address range matches lines starting from where the first address matches, and continues until the second address matches (inclusively):$ seq 10 | sed -n '4,6p' 4 5 6
If the second address is a regexp, then checking for the ending match will start with the line following the line which matched the first address: a range will always span at least two lines (except of course if the input stream ends).
…
- [Wayback] Regexp Addresses (sed, a stream editor)
For example (with some characters escaped because of [Wayback] ERE syntax (sed, a stream editor)):
# vim-cmd vmsvc/get.config 10 | sed -n -E -e '/\(vim.vm.ConfigInfo\) \{/,/files = \(vim.vm.FileInfo\) \{/p' (vim.vm.ConfigInfo) { changeVersion = "2021-04-07T22:08:30.548274Z", modified = "1970-01-01T00:00:00Z", name = "X9SRI-3F-W10P-EN-MEDIA", guestFullName = "Microsoft Windows 10 (64-bit)", version = "vmx-14", uuid = "564d51ac-f6cf-e40b-b686-2f53a28a4bea", createDate = "2019-05-17T21:37:11.408173Z", instanceUuid = "52403d0e-7ccd-48da-bb21-7e966defccf7", npivNodeWorldWideName = , npivPortWorldWideName = , npivWorldWideNameType = , npivDesiredNodeWwns = , npivDesiredPortWwns = , npivTemporaryDisabled = true, npivOnNonRdmDisks = , locationId = "564d6b18-ecd1-2261-0127-146b3f3bc636", template = false, guestId = "windows9_64Guest", alternateGuestName = "", annotation = "", files = (vim.vm.FileInfo) {
With [Wayback] BRE syntax (sed, a stream editor) the filter part would be easier: vim-cmd vmsvc/get.config 10 | sed -n -e '/(vim.vm.ConfigInfo) {/,/files = (vim.vm.FileInfo) {/p'
, but the print part would be more difficult:
-
# vim-cmd vmsvc/get.config 10 | sed -n -E -e '/\(vim.vm.ConfigInfo\) \{/,/files = \(vim.vm.FileInfo\) \{/ s/^ +name = "(.*)",.*?/1/p' X9SRI-3F-W10P-EN-MEDIA
-
# vim-cmd vmsvc/get.config 10 | sed -n -e '/(vim.vm.ConfigInfo) {/,/files = (vim.vm.FileInfo) {/ s/^ +name = "(.*)",.*?/1/p' X9SRI-3F-W10P-EN-MEDIA
Since I am used to extended regular expressions (ERE) over basica regular expressions (BRE), I prefer the first solution.
So getting the name in a variable now becomes this:
# name=`vim-cmd vmsvc/get.config 10 | sed -n -e '/(vim.vm.ConfigInfo) {/,/files = (vim.vm.FileInfo) {/ s/^ +name = "(.*)",.*?/1/p'` # echo ${name} X9SRI-3F-W10P-EN-MEDIA
List the vmid
values, power status and name of all VMs
Back to the listing script vim-cmd-list-all-VMs.sh
:
#!/bin/sh # https://wiert.me/2021/04/29/vmware-esxi-console-viewing-all-vms-suspending-and-waking-them-up-part-4/ vmids=`vim-cmd vmsvc/getallvms | sed -n -E -e "s/^([[:digit:]]+)\s+((\S.+\S)?)\s+(\[\S+\])\s+(.+\.vmx)\s+(\S+)\s+(vmx-[[:digit:]]+)\s*?((\S.+)?)$/\1/p"` for vmid in ${vmids} ; do powerState=`vim-cmd vmsvc/power.getstate ${vmid} | sed '1d'` name=`vim-cmd vmsvc/get.config ${vmid} | sed -n -E -e '/\(vim.vm.ConfigInfo\) \{/,/files = \(vim.vm.FileInfo\) \{/ s/^ +name = "(.*)",.*?/\1/p'` vmPathName=`vim-cmd vmsvc/get.config ${vmid} | sed -n -E -e '/files = \(vim.vm.FileInfo\) \{/,/tools = \(vim.vm.ToolsConfigInfo\) \{/ s/^ +vmPathName = "(.*)",.*?/\1/p'` echo "VM with id ${vmid} has power state ${powerState} (name = ${name}; vmPathName = ${vmPathName})." done
As a bonus, next to powerState
, the script also figures out vmPathName
in a similar way to name
.
–jeroen
Leave a Reply