Skip to content

Instantly share code, notes, and snippets.

@tensorfields
Created January 3, 2013 22:02
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save tensorfields/4447791 to your computer and use it in GitHub Desktop.
Save tensorfields/4447791 to your computer and use it in GitHub Desktop.
Get a KVM guest's IP address
#!/bin/bash
# Returns the IP address of a running KVM guest VM
# Assumes a working KVM/libvirt environment
#
# Install:
# Add this bash function to your ~/.bashrc and `source ~/.bashrc`.
# Usage:
# $ virt-addr vm-name
# 192.0.2.16
#
virt-addr() {
VM="$1"
arp -an | grep "`virsh dumpxml $VM | grep "mac address" | sed "s/.*'\(.*\)'.*/\1/g"`" | awk '{ gsub(/[\(\)]/,"",$2); print $2 }'
}
@kevteljeur
Copy link

I know that this an old script, but still works well. I'm guessing others come here looking for it for the same reason as me. This was very useful for me. However, I found that more often, I wanted to see the list of all the addresses for all the running VMs, without having to do the list and then query separately. Here is my solution to that, using your code:

virt-addrs() {
    echo "----------------------------------------------";
    # Print column headers
    printf "%-30s %s\n" "VM Name" "IP Address";
    # Output the system list (shouldn't be using the --all flag, but hey)
    virsh -c qemu:///system list --all | grep -o '[0-9]* [a-z]*.*running' | while read -r line;
    do
        # Get the VM names and then pass them to virt-addr
        line_cropped=$(echo "$line" | sed 's/[0-9][ ]*\([-._0-9a-zA-Z]*\)[ ]*running/\1/' );
        printf "%-30s %s\n" "$line_cropped" $( virt-addr "$line_cropped" );
    done;
    echo "----------------------------------------------";
}

It could be better but I'm not a shell wizard, and I didn't need more at the time of writing. It probably could display more for each VM using the same technique (such as Mac address and some basic stats, RAM & drive limits, etc).

@kevteljeur
Copy link

Here's an updated, 'extended mix' which parses out a whole lot more information. It is a bit slow, however, so I'll look at doing some caching of the XML to speed it all up...

virt-uuid() {
    VM="$1"
    uuid=$(virsh dumpxml $VM | grep "<uuid" | sed "s/.*<uuid>\(.*\)<\/uuid>.*/\1/g" );
    echo $uuid
}

virt-mem() {
    VM="$1"
    mem=$(virsh dumpxml $VM | grep "<memory" | sed "s/.*<memory unit='KiB'>\(.*\)<\/memory>.*/\1/g" );
    echo $( expr $mem / 1024 )
}

virt-currmem() {
    VM="$1"
    mem=$(virsh dumpxml $VM | grep "<currentMemory" | sed "s/.*<currentMemory unit='KiB'>\(.*\)<\/currentMemory>.*/\1/g" );
    echo $( expr $mem / 1024 )
}

virt-vcpu() {
    VM="$1"
    vcpu=$(virsh dumpxml $VM | grep "<vcpu" | sed "s/.*<vcpu[^>]*>\(.*\)<\/vcpu>.*/\1/g" );
    echo $vcpu
}

virt-store() {
    VM="$1"
   # Oops! Not implemented yet, but basically this will retrieve the path to the VM file. Might not be true in all cases, however.
}

virt-info() {
    echo "------------------------------------------------------------------------------------------------------------------------";
    printf "%-30s%-17s%-12s%-12s%-8s%-40s\n" "VM Name" "IP Address" "Memory" "Current" "VCPUs" "UUID";
    virsh -c qemu:///system list --all | grep -o '[0-9]* [a-z]*.*running' | while read -r line;
    do
        line_cropped=$(echo "$line" | sed 's/[0-9][ ]*\([-._0-9a-zA-Z]*\)[ ]*running/\1/' );
        printf "%-30s%-17s%-12s%-12s%-8s%-40s\n" "$line_cropped" $( virt-addr "$line_cropped" ) $( virt-mem $line ) $( virt-currmem $line ) $( virt-vcpu $line ) $( virt-uuid $line );
    done;
    echo "------------------------------------------------------------------------------------------------------------------------";
}

@kevteljeur
Copy link

I've forked it here: https://gist.github.com/kevteljeur/ad73ca9ca0cfe8f14458 - with some performance improvements to my listing code (it really is slow when re-reading the same file over and over) and it now also displays info for VMs that aren't running. Hope it helps someone, and if you're reading this and have a faster way to do anything, please share!

@dehai
Copy link

dehai commented May 11, 2016

if the VMs network is bridge network. the script doesn't work with it. because ' arp -an' cann't find those VMs IP and MAC , could anybody help on this. thanks!!

@dehai
Copy link

dehai commented May 12, 2016

I found a way to solve my problem. but need do something on virt-manager or virsh edit domain
1:using virt-manager , need add new hardware with channel. channel device need named of org.qemu.guest_agent.0. make sure the guest VM already installed the qemu-guest-agent package. enable and start this service of qemu-ga.service . if you have done those two process . that you can use the script to show the VMs IP.
2: using virsh command , just same with virt-manager. only edit the domain XML file. you can use virsh edit your-VM-domain.add the below hardware info:

<channel type='unix'>
      <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/**your-domain-name**.org.qemu.guest_agent.0'/>
      <target type='virtio' name='org.qemu.guest_agent.0' state='connected'/>
      <alias name='channel0'/>
      <address type='virtio-serial' controller='0' bus='0' port='1'/>
    </channel>

after that: you can follow the method of using virt-manager. make sure installed qemu-guest-agent, and enable service of qemu-ga.service

@lukehinds
Copy link

lukehinds commented Jun 8, 2016

Note there is now a command which returns IPs:

virsh domifaddr <domain> [interface] [–full] [–source lease|agent]

Example:

 # virsh domifaddr fedora_vm --full
 Name       MAC address          Protocol     Address
 -------------------------------------------------------------------------------
 vnet0      52:54:00:2e:45:ce    ipv6         2001:db8:0:f101::2/64
 vnet1      52:54:00:b1:70:19    ipv4         192.168.105.201/16
 vnet1      52:54:00:b1:70:19    ipv6         2001:db8:ca2:2:1::bd/128
 vnet3      52:54:00:20:70:3d    ipv4         192.168.105.240/16

And for DHCP leases:

# virsh net-dhcp-leases --network default
Expiry Time          MAC address        Protocol  IP address                Hostname        Client ID or DUID
-------------------------------------------------------------------------------------------------------------------
2014-06-16 03:40:14  52:54:00:85:90:e2  ipv4      192.168.150.231/24        fedora20-test   01:52:54:00:85:90:e2
2014-06-16 03:40:17  52:54:00:85:90:e2  ipv6      2001:db8:ca2:2:1::c0/64   fedora20-test   00:04:b1:d8:86:42:e1:6a:aa:cf:d5:86:94:23:6f:94:04:cd
2014-06-16 03:34:42  52:54:00:e8:73:eb  ipv4      192.168.150.181/24        ubuntu14-vm     -
2014-06-16 03:34:46  52:54:00:e8:73:eb  ipv6      2001:db8:ca2:2:1::5b/64   -               00:01:00:01:1b:30:c6:aa:52:54:00:e8:73:eb  

@marafa
Copy link

marafa commented Jan 7, 2017

VM=fedora25;
sudo virsh domifaddr $VM|tail -2 | awk '{print $4}'| cut -d/ -f1

i am sure a bash expert can make that line even better but it works for me and "if its not broken don't fix it" is a good rule for me

@norpol
Copy link

norpol commented May 22, 2017

@marafa and rest here is an awk only script:
virsh domifaddr $VM | awk -F'[ /]+' '{if (NR>2) print $5}'

@IAlwaysBeCoding
Copy link

virt-info() {
    echo "------------------------------------------------------------------------------------------------------------------------";
    printf "%-30s%-17s%-12s%-12s%-8s%-40s\n" "VM Name" "IP Address" "Memory" "Current" "VCPUs" "UUID";
    virsh -c qemu:///system list --all | grep -o '[0-9]* [a-z]*.*running' | while read -r line;
    do
        line_cropped=$(echo "$line" | sed 's/[0-9][ ]*\([-._0-9a-zA-Z]*\)[ ]*running/\1/' );
        printf "%-30s%-17s%-12s%-12s%-8s%-40s\n" "$line_cropped" $( virt-addr "$line_cropped" ) $( virt-mem $line ) $( virt-currmem $line ) $( virt-vcpu $line ) $( virt-uuid $line );
    done;
    echo "------------------------------------------------------------------------------------------------------------------------";
}

This breaks if the ID is higher than 9

Change this line

line_cropped=$(echo "$line" | sed 's/[0-9][ ]*\([-._0-9a-zA-Z]*\)[ ]*running/\1/' );

To

line_cropped=$(echo "$line" | sed 's/[0-9]*[ ]*\([-._0-9a-zA-Z]*\)[ ]*running/\1/' );

@elico
Copy link

elico commented Jul 18, 2018

@mistofvongola and the others.
If you have qmeu-agent installed on the guest machine you can user virsh to fetch the details directly from the VM using virsh.
Example commad: virsh domifaddr Squid-4.1-CentOS-Testing --source agent --full (the --source agent flag is important)

[root@kvm4 ~]# virsh list
 Id    Name                           State
----------------------------------------------------
 1     KVM4-MANAGER                   running
 2     Fast-Windows-10                running
 3     CHR-LAB-CLIENT1                running
 4     CHR-LAB-PX3                    running
 5     CHR-LAB-SERVER1                running
 6     CHR-R1                         running
 7     Squid-4.1-CentOS-Testing       running

[root@kvm4 ~]# virsh domifaddr Squid-4.1-CentOS-Testing --source agent --full
Name       MAC address          Protocol     Address
-------------------------------------------------------------------------------
 lo         00:00:00:00:00:00    ipv4         127.0.0.1/8
 lo         00:00:00:00:00:00    ipv6         ::1/128
 eth0       52:54:00:7e:7f:29    ipv4         192.168.89.59/24
 eth0       52:54:00:7e:7f:29    ipv6         fe80::125d:b868:7bf7:11f0/64
 eth1       52:54:00:18:05:6b    N/A          N/A
 eth2       52:54:00:fd:79:85    N/A          N/A
 eth3       52:54:00:cc:ac:c8    N/A          N/A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment