Reputation: 3024
Is there a way to force virsh
to print information in a parseable way? like json
?
I want to write a one-liner shell command that gets the IP address of a VM but the way virsh prints it out is not very friendly to scripts:
# virsh domifaddr myvm
Name MAC address Protocol Address
-------------------------------------------------------------------------------
vnet1 52:54:00:b9:58:64 ipv4 192.168.130.156/24
I'm looking for a way to force it to not print the headers at least so I can get '192.168.130.156' from the output easily
This is the best I could do:
# virsh -q domifaddr myvm | awk '{print $4}' | cut -d/ -f 1
192.168.130.156
Upvotes: 3
Views: 2887
Reputation: 39195
The virsh domifaddr
command doesn't provide a machine readable output mode, unfortunately.
However, you can use the libvirt Python API for a one-liner that gets the IP address of your guest:
python -c 'import sys; import libvirt; con = libvirt.open("qemu:///system"); dom = con.lookupByName(sys.argv[1]); print(dom.interfaceAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE).popitem()[1]["addrs"][-1]["addr"])' myvm
It basically a light version of virsh domifaddr
.
NB: The libvirt Python package likely is install on a system where libvirt is available, because virt-install and virt-manager depend on it. (at least on Fedora systems)
Upvotes: 0
Reputation: 4284
I ran into this today, so I wrote a quick program to do it.
https://github.com/a-h/virshjson
The output tables are fairly easy to parse, by looking for the header which contains the column names, and noting the positions, skipping the line of hyphens which is output, and then using the stored positions to extract fields.
package virshjson
import (
"bufio"
"errors"
"io"
"regexp"
"strings"
)
var ErrMalformedHeader = errors.New("malformed input header")
var ErrMalformedSeparator = errors.New("malformed input separator")
var ErrMalformedBody = errors.New("malformed input body")
type field struct {
name string
start int
}
var fieldsRegexp = regexp.MustCompile(`.+?(\s{2,}|$)`)
func getFields(s string) (fields []field) {
matches := fieldsRegexp.FindAllStringIndex(s, -1)
for _, match := range matches {
start, end := match[0], match[1]
fields = append(fields, field{
name: strings.TrimSpace(s[start:end]),
start: start,
})
}
return fields
}
var separatorRegexp = regexp.MustCompile(`^\-+$`)
func Convert(input io.Reader) ([]map[string]any, error) {
scanner := bufio.NewScanner(input)
// Read headers.
scanner.Scan()
if scanner.Err() != nil {
return nil, scanner.Err()
}
fields := getFields(scanner.Text())
if len(fields) == 0 {
return nil, ErrMalformedHeader
}
// Read the line of hyphens.
scanner.Scan()
if scanner.Err() != nil {
return nil, scanner.Err()
}
if !separatorRegexp.MatchString(scanner.Text()) {
return nil, ErrMalformedSeparator
}
// Read lines until an empty one.
data := make([]map[string]any, 0)
for scanner.Scan() {
if scanner.Err() != nil {
return nil, scanner.Err()
}
if len(scanner.Text()) == 0 {
continue
}
value := make(map[string]any)
for i, field := range fields {
end := len(scanner.Text())
if i < len(fields)-1 {
end = fields[i+1].start
}
value[field.name] = strings.TrimSpace(scanner.Text()[field.start:end])
}
data = append(data, value)
}
return data, nil
}
Given input:
Name MAC address Protocol Address
-------------------------------------------------------------------------------
vnet1 52:54:00:c9:ae:a5 ipv4 192.168.122.110/24
The tool outputs:
[
{
"Address": "192.168.122.110/24",
"MAC address": "52:54:00:c9:ae:a5",
"Name": "vnet1",
"Protocol": "ipv4"
}
]
Upvotes: 0
Reputation: 41
One option is to install qemu-guest-agent
on the domains you would like to extract IP information from.
From there, you can execute the following command on the host to get a detailed network interface listing in JSON:
ubuntu@host:~$ virsh qemu-agent-command my-guest '{"execute":"guest-network-get-interfaces"}'
{"return":[{"name":"lo","ip-addresses":[{"ip-address-type":"ipv4","ip-address":"127.0.0.1","prefix":8},{"ip-address-type":"ipv6","ip-address":"::1","prefix":128}],"statistics":{"tx-packets":22,"tx-errs":0,"rx-bytes":2816,"rx-dropped":0,"rx-packets":22,"rx-errs":0,"tx-bytes":2816,"tx-dropped":0},"hardware-address":"00:00:00:00:00:00"},{"name":"eth0","ip-addresses":[{"ip-address-type":"ipv4","ip-address":"1.2.3.4","prefix":22},{"ip-address-type":"ipv6","ip-address":"abcd::1234:ee:ab12:e31d","prefix":64}],"statistics":{"tx-packets":11231,"tx-errs":0,"rx-bytes":40717370,"rx-dropped":0,"rx-packets":19744,"rx-errs":0,"tx-bytes":890354,"tx-dropped":0},"hardware-address":"01:02:00:03:04:05"}]}
Your json can be parsed however you'd like from there.
Upvotes: 2