libvirt: Support IPv6 with LXC

Libvirt's LXC implementation exposes a read-only  `/proc/sys/net` to the
guests. This causes some of the guest's default network configuration scripts
to fail.

This patch works-around the issue by using `post-up` hooks to configure IPv6.

Closes-Bug: 1340791

Change-Id: I805cad98d855fcb2c90b07e98ad3653d1620bd42
This commit is contained in:
Rick Harris 2014-07-09 17:17:50 -05:00
parent 2b5c43d23b
commit d0248d0621
4 changed files with 88 additions and 6 deletions

View File

@ -506,7 +506,8 @@ class NetworkInfoTests(test.NoDBTestCase):
def _setup_injected_network_scenario(self, should_inject=True,
use_ipv4=True, use_ipv6=False,
gateway=True, dns=True,
two_interfaces=False):
two_interfaces=False,
libvirt_virt_type=None):
"""Check that netutils properly decides whether to inject based on
whether the supplied subnet is static or dynamic.
"""
@ -548,8 +549,8 @@ class NetworkInfoTests(test.NoDBTestCase):
vifs.append(vif)
nwinfo = model.NetworkInfo(vifs)
return netutils.get_injected_network_template(nwinfo,
use_ipv6=use_ipv6)
return netutils.get_injected_network_template(
nwinfo, use_ipv6=use_ipv6, libvirt_virt_type=libvirt_virt_type)
def test_injection_dynamic(self):
expected = None
@ -714,3 +715,70 @@ iface eth1 inet6 static
template = self._setup_injected_network_scenario(use_ipv6=True,
two_interfaces=True)
self.assertEqual(expected, template)
def test_injection_ipv6_with_lxc(self):
expected = """\
# Injected by Nova on instance boot
#
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 10.10.0.2
netmask 255.255.255.0
broadcast 10.10.0.255
gateway 10.10.0.1
dns-nameservers 1.2.3.4 2.3.4.5
post-up ip -6 addr add 1234:567::2/48 dev ${IFACE}
post-up ip -6 route add default via 1234:567::1 dev ${IFACE}
auto eth1
iface eth1 inet static
address 10.10.0.2
netmask 255.255.255.0
broadcast 10.10.0.255
gateway 10.10.0.1
dns-nameservers 1.2.3.4 2.3.4.5
post-up ip -6 addr add 1234:567::2/48 dev ${IFACE}
post-up ip -6 route add default via 1234:567::1 dev ${IFACE}
"""
template = self._setup_injected_network_scenario(
use_ipv6=True, two_interfaces=True, libvirt_virt_type='lxc')
self.assertEqual(expected, template)
def test_injection_ipv6_with_lxc_no_gateway(self):
expected = """\
# Injected by Nova on instance boot
#
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 10.10.0.2
netmask 255.255.255.0
broadcast 10.10.0.255
dns-nameservers 1.2.3.4 2.3.4.5
post-up ip -6 addr add 1234:567::2/48 dev ${IFACE}
auto eth1
iface eth1 inet static
address 10.10.0.2
netmask 255.255.255.0
broadcast 10.10.0.255
dns-nameservers 1.2.3.4 2.3.4.5
post-up ip -6 addr add 1234:567::2/48 dev ${IFACE}
"""
template = self._setup_injected_network_scenario(
use_ipv6=True, gateway=False, two_interfaces=True,
libvirt_virt_type='lxc')
self.assertEqual(expected, template)

View File

@ -20,6 +20,14 @@ iface {{ ifc.name }} inet static
dns-nameservers {{ ifc.dns }}
{% endif %}
{% if use_ipv6 %}
{% if libvirt_virt_type == 'lxc' %}
{% if ifc.address_v6 %}
post-up ip -6 addr add {{ ifc.address_v6 }}/{{ifc.netmask_v6 }} dev ${IFACE}
{% endif %}
{% if ifc.gateway_v6 %}
post-up ip -6 route add default via {{ ifc.gateway_v6 }} dev ${IFACE}
{% endif %}
{% else %}
iface {{ ifc.name }} inet6 static
address {{ ifc.address_v6 }}
netmask {{ ifc.netmask_v6 }}
@ -27,4 +35,5 @@ iface {{ ifc.name }} inet6 static
gateway {{ ifc.gateway_v6 }}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}

View File

@ -2574,7 +2574,8 @@ class LibvirtDriver(driver.ComputeDriver):
admin_pass = None
# Handles the network injection.
net = netutils.get_injected_network_template(network_info)
net = netutils.get_injected_network_template(
network_info, libvirt_virt_type=CONF.libvirt.virt_type)
# Handles the metadata injection
metadata = instance.get('metadata')

View File

@ -59,7 +59,8 @@ def _get_first_network(network, version):
def get_injected_network_template(network_info, use_ipv6=CONF.use_ipv6,
template=CONF.injected_network_template):
template=CONF.injected_network_template,
libvirt_virt_type=None):
"""Returns a rendered network template for the given network_info.
:param network_info:
@ -67,6 +68,8 @@ def get_injected_network_template(network_info, use_ipv6=CONF.use_ipv6,
:param use_ipv6: If False, do not return IPv6 template information
even if an IPv6 subnet is present in network_info.
:param template: Path to the interfaces template file.
:param libvirt_virt_type: The Libvirt `virt_type`, will be `None` for
other hypervisors..
"""
if not (network_info and template):
return
@ -145,4 +148,5 @@ def get_injected_network_template(network_info, use_ipv6=CONF.use_ipv6,
trim_blocks=True)
template = env.get_template(tmpl_file)
return template.render({'interfaces': nets,
'use_ipv6': ipv6_is_available})
'use_ipv6': ipv6_is_available,
'libvirt_virt_type': libvirt_virt_type})