Adding networks to libvirt
Table of Contents
For my Swiftacular project I’d like to figure out if I can run some automated tests and create a new Swiftacular cluster using libvirt. My Swiftacular configuration currently has five networks used to create an OpenStack Swift cluster (complete with a separate replication network). My Vagrantfile for Swiftacular sets up those networks, so I need to replicate that configuration using libvirt.
By default with a libvirt host on Ubuntu 12.04 you get one network: the default network.
# virsh net-list
Name State Autostart
-----------------------------------------
default active yes
That default network comes with a bridge and a dnsmasq server to provide dhcp addresses.
# ifconfig | grep virbr
virbr0 Link encap:Ethernet HWaddr 52:54:00:a3:05:65
# ps ax |grep dns
4943 ? S 0:02 /usr/sbin/dnsmasq -u libvirt-dnsmasq
SNIP!
Each virtual machine booted using libvirt will have a default network configured. Below is a snippet of the xml defining a virtual machine. Note how the network to use is set to “default.”
SNIP!
SNIP!
So first we need to add more networks, and then we need to configure virtual machines xml definition file with the networks, and then ensure that the vms has more interfaces set up.
More libvirt networks
In Swiftacular I have five networks:
- eth0 - default - Used by Vagrant
- eth1 - public - 192.168.100.0/24 - The “public” network that users would connect to
- eth2 - lbssl - 10.0.10.0/24 - This is the network between the SSL terminator and the Swift Proxy
- eth3 - internal - 10.0.20.0/24 - The local Swift internal network
- eth4 - replication - 10.0.30.0/24 - The replication network which is a feature of OpenStack Swift starting with the Havana release
I’m going to replicate that with libvirt networks, and the eth0 network will be the default network.
Here is an xml definition file for the internal network I would like to add:
internal
Now that the xml file has been created, we can use virsh net-define to define it.
# virsh net-define internal_network.xml
Network internal defined from internal_network.xml
And we can see the network show up in virsh net-list.
# virsh net-list --all
Name State Autostart
-----------------------------------------
default active yes
internal inactive no
But as can bee seen above, the network is not active, nor set to autostart. So let’s do that.
# virsh net-start internal
Network internal started
# virsh net-autostart internal
Network internal marked as autostarted
# virsh net-list
Name State Autostart
-----------------------------------------
default active yes
internal active yes
Also we have an internal bridge and a dnsmasq process for that network as well which sets up a leases file.
# brctl show |grep internal
internalbr0 8000.525400185a02 yes internalbr0-nic
# ls /var/lib/libvirt/dnsmasq/internal.leases
/var/lib/libvirt/dnsmasq/internal.leases
I’m going to setup the rest of my networks in the same fashion.
# virsh net-list
Name State Autostart
-----------------------------------------
default active yes
internal active yes
lbssl active yes
private active yes
replication active yes
Ok, now we can move onto the other steps.
Add network interfaces to libvirt vm definition file
In order for those networks to be available to a virtual machine they need to be configured in the xml file that defines the vm.
SNIP!
SNIP!
That snippet would have to be in every vm definition file.
Cloud-init and user-data
I use cloud-init to help configure vms in libvirt.
I have a user-data and meta-data file for each vm which is converted into an image file and connected to the virtual machine.
In my user-data file I configure the files that will setup eth2 to eth4 as dhcp. eth1 is already setup in the image, so I don’t have to configure it with cloud-init.
SNIP!
write_files:
- content: |
auto eth1
iface eth1 inet dhcp
path: /etc/network/interfaces.d/eth1.cfg
- content: |
auto eth2
iface eth2 inet dhcp
path: /etc/network/interfaces.d/eth2.cfg
- content: |
auto eth3
iface eth3 inet dhcp
path: /etc/network/interfaces.d/eth3.cfg
- content: |
auto eth4
iface eth4 inet dhcp
path: /etc/network/interfaces.d/eth4.cfg
SNIP!
When the vm boots with a cloud-init image attached, the cloud-init client on the vm will setup the files as configured so that each new interface on the new networks will get an IP from dnsmasq on the corresponding bridge.
Completely going over how cloud-init works is beyond the scope of this blog post, but it is a very important part of any cloud or virtualization platform. Ok, well not every virtualization system. OpenStack too though. :)
Boot a vm
Let’s boot a test vm.
I have a script called generic.sh that creates a custom cloud-init image and configures the image in the libvirt xml file for the vm.
I am using Ubuntu 14.04 as the OS for the virtual machine. This has a different cloud-init version that Ubuntu 12.04 so there may be differences in terms of cloud-init if trying to boot a Precise vm vs a Trusty vm.
# ./generic.sh trusty testnetworks
Domain testnetworks defined from /tmp/tmp.yJ6w0CO3A2
Domain testnetworks started
Now I have a vm called “testnetworks” running in libvirt. I can check the various dnsmasq lease files in /var/lib/libvirt/dnsmasq/*.leases to see if it got an IP address.
# virsh list |grep test
40 testnetworks running
# cat /var/lib/libvirt/dnsmasq/internal.leases 1398535697 52:54:00:72:db:0e 10.0.20.68 testnetworks *
And we can see that it did.
Also we can find out the macs that were randomly given to the vm via libvirt using virsh dumpxml.
# virsh dumpxml testnetworks | grep mac
hvm
If I ssh into the virtual machine I should be able to see all those interfaces and checkout the routing table. I can also get to the Internet via the default gateway.
# ssh ubuntu@10.0.20.68
SNIP!
ubuntu@testnetworks:~$ ifconfig | grep eth
eth0 Link encap:Ethernet HWaddr 52:54:00:2d:52:8d
eth1 Link encap:Ethernet HWaddr 52:54:00:72:db:0e
eth2 Link encap:Ethernet HWaddr 52:54:00:93:1b:07
eth3 Link encap:Ethernet HWaddr 52:54:00:76:61:ee
eth4 Link encap:Ethernet HWaddr 52:54:00:72:79:48
ubuntu@testnetworks:~$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.122.1 0.0.0.0 UG 0 0 0 eth0
10.0.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2
10.0.20.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
10.0.30.0 0.0.0.0 255.255.255.0 U 0 0 0 eth4
192.168.100.0 0.0.0.0 255.255.255.0 U 0 0 0 eth3
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
ubuntu@testnetworks:~$ ping -c 1 news.google.com
PING news.l.google.com (74.125.228.100) 56(84) bytes of data.
64 bytes from iad23s08-in-f4.1e100.net (74.125.228.100): icmp_seq=1 ttl=52 time=96.2 ms
--- news.l.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 96.265/96.265/96.265/0.000 ms
So now I have five networks to play with!
Let’s boot one more to see if we have connectivity between two vms.
# ./generic.sh trusty testnetworks2
# cat /var/lib/libvirt/dnsmasq/internal.leases
1398535915 52:54:00:84:c2:b9 10.0.20.27 testnetworks2 *
1398535697 52:54:00:72:db:0e 10.0.20.68 testnetworks *
So we should be able to ping 10.0.20.27 from .68.
ubuntu@testnetworks:~$ ping -c 1 10.0.20.27
PING 10.0.20.27 (10.0.20.27) 56(84) bytes of data.
64 bytes from 10.0.20.27: icmp_seq=1 ttl=64 time=0.284 ms
--- 10.0.20.27 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.284/0.284/0.284/0.000 ms
Yup.
So now we have five networks in libvirt, and virtual machines that can boot up and get a dhcp address on each of those networks. Hopefully this means I can work on automating testing of Swiftacular, probably by creating a custom inventory script.
Please let me know if you see any issues. One question I have is about the forward mode that should be set. I’m not sure it should be nat for my extra networks. Something to look into.