Merge "Build Neutron network data for metadata service"

This commit is contained in:
Jenkins 2015-07-11 01:33:21 +00:00 committed by Gerrit Code Review
commit 076262b462
2 changed files with 470 additions and 0 deletions

View File

@ -846,3 +846,297 @@ iface eth1 inet static
use_ipv6=True, gateway=False, two_interfaces=True,
libvirt_virt_type='lxc')
self.assertEqual(expected, template)
class TestNetworkMetadata(test.NoDBTestCase):
def setUp(self):
super(TestNetworkMetadata, self).setUp()
self.netinfo = model.NetworkInfo([fake_network_cache_model.new_vif(
{'type': 'ethernet'})])
# Give this vif ipv4 and ipv6 dhcp subnets
ipv4_subnet = fake_network_cache_model.new_subnet(version=4)
ipv6_subnet = fake_network_cache_model.new_subnet(version=6)
self.netinfo[0]['network']['subnets'][0] = ipv4_subnet
self.netinfo[0]['network']['subnets'][1] = ipv6_subnet
self.netinfo[0]['network']['mtu'] = 1500
def test_get_network_metadata_json(self):
net_metadata = netutils.get_network_metadata(self.netinfo,
use_ipv6=True)
# Physical Ethernet
self.assertEqual(
{
'id': 'interface0',
'type': 'phy',
'ethernet_mac_address': 'aa:aa:aa:aa:aa:aa',
'vif_id': 1,
'mtu': 1500
},
net_metadata['links'][0])
# IPv4 Network
self.assertEqual(
{
'id': 'network0',
'link': 'interface0',
'type': 'ipv4',
'ip_address': '10.10.0.2',
'netmask': '255.255.255.0',
'routes': [
{
'network': '0.0.0.0',
'netmask': '0.0.0.0',
'gateway': '10.10.0.1'
},
{
'network': '0.0.0.0',
'netmask': '255.255.255.0',
'gateway': '192.168.1.1'
}
],
'network_id': 1
},
net_metadata['networks'][0])
self.assertEqual(
{
'id': 'network1',
'link': 'interface0',
'type': 'ipv6',
'ip_address': 'fd00::2',
'netmask': 'ffff:ffff:ffff::',
'routes': [
{
'network': '::',
'netmask': '::',
'gateway': 'fd00::1'
},
{
'network': '::',
'netmask': 'ffff:ffff:ffff::',
'gateway': 'fd00::1:1'
}
],
'network_id': 1
},
net_metadata['networks'][1])
def test_get_network_metadata_json_dhcp(self):
ipv4_subnet = fake_network_cache_model.new_subnet(
subnet_dict=dict(dhcp_server='1.1.1.1'), version=4)
ipv6_subnet = fake_network_cache_model.new_subnet(
subnet_dict=dict(dhcp_server='1234:567::'), version=6)
self.netinfo[0]['network']['subnets'][0] = ipv4_subnet
self.netinfo[0]['network']['subnets'][1] = ipv6_subnet
net_metadata = netutils.get_network_metadata(self.netinfo,
use_ipv6=True)
# IPv4 Network
self.assertEqual(
{
'id': 'network0',
'link': 'interface0',
'type': 'ipv4_dhcp',
'network_id': 1
},
net_metadata['networks'][0])
# IPv6 Network
self.assertEqual(
{
'id': 'network1',
'link': 'interface0',
'type': 'ipv6_dhcp',
'network_id': 1
},
net_metadata['networks'][1])
def test__get_nets(self):
expected_net = {
'id': 'network0',
'ip_address': '10.10.0.2',
'link': 1,
'netmask': '255.255.255.0',
'network_id': 1,
'routes': [
{
'gateway': '10.10.0.1',
'netmask': '0.0.0.0',
'network': '0.0.0.0'},
{
'gateway': '192.168.1.1',
'netmask': '255.255.255.0',
'network': '0.0.0.0'}],
'type': 'ipv4'
}
net = netutils._get_nets(
self.netinfo[0], self.netinfo[0]['network']['subnets'][0], 4, 0, 1)
self.assertEqual(expected_net, net)
def test__get_eth_link(self):
expected_link = {
'id': 'interface0',
'vif_id': 1,
'type': 'vif',
'ethernet_mac_address': 'aa:aa:aa:aa:aa:aa',
'mtu': 1500
}
self.netinfo[0]['type'] = 'vif'
link = netutils._get_eth_link(self.netinfo[0], 0)
self.assertEqual(expected_link, link)
def test__get_eth_link_physical(self):
expected_link = {
'id': 'interface1',
'vif_id': 1,
'type': 'phy',
'ethernet_mac_address': 'aa:aa:aa:aa:aa:aa',
'mtu': 1500
}
link = netutils._get_eth_link(self.netinfo[0], 1)
self.assertEqual(expected_link, link)
def test__get_default_route(self):
v4_expected = [{
'network': '0.0.0.0',
'netmask': '0.0.0.0',
'gateway': '10.10.0.1',
}]
v6_expected = [{
'network': '::',
'netmask': '::',
'gateway': 'fd00::1'
}]
v4 = netutils._get_default_route(
4, self.netinfo[0]['network']['subnets'][0])
self.assertEqual(v4_expected, v4)
v6 = netutils._get_default_route(
6, self.netinfo[0]['network']['subnets'][1])
self.assertEqual(v6_expected, v6)
# Test for no gateway
self.netinfo[0]['network']['subnets'][0]['gateway'] = None
no_route = netutils._get_default_route(
4, self.netinfo[0]['network']['subnets'][0])
self.assertEqual([], no_route)
def test__get_dns_services(self):
expected_dns = [
{'type': 'dns', 'address': '1.2.3.4'},
{'type': 'dns', 'address': '2.3.4.5'},
{'type': 'dns', 'address': '3.4.5.6'}
]
subnet = fake_network_cache_model.new_subnet(version=4)
subnet['dns'].append(fake_network_cache_model.new_ip(
{'address': '3.4.5.6'}))
dns = netutils._get_dns_services(subnet)
self.assertEqual(expected_dns, dns)
def test_get_network_metadata(self):
expected_json = {
"links": [
{
"ethernet_mac_address": "aa:aa:aa:aa:aa:aa",
"id": "interface0",
"type": "phy",
"vif_id": 1,
"mtu": 1500
},
{
"ethernet_mac_address": "aa:aa:aa:aa:aa:ab",
"id": "interface1",
"type": "phy",
"vif_id": 1,
"mtu": 1500
},
],
"networks": [
{
"id": "network0",
"ip_address": "10.10.0.2",
"link": "interface0",
"netmask": "255.255.255.0",
"network_id":
"00000000-0000-0000-0000-000000000000",
"routes": [
{
"gateway": "10.10.0.1",
"netmask": "0.0.0.0",
"network": "0.0.0.0"
},
{
"gateway": "192.168.1.1",
"netmask": "255.255.255.0",
"network": "0.0.0.0"
}
],
"type": "ipv4"
},
{
'id': 'network1',
'ip_address': 'fd00::2',
'link': 'interface0',
'netmask': 'ffff:ffff:ffff::',
'network_id': '00000000-0000-0000-0000-000000000000',
'routes': [{'gateway': 'fd00::1',
'netmask': '::',
'network': '::'},
{'gateway': 'fd00::1:1',
'netmask': 'ffff:ffff:ffff::',
'network': '::'}],
'type': 'ipv6'
},
{
"id": "network2",
"ip_address": "192.168.0.2",
"link": "interface1",
"netmask": "255.255.255.0",
"network_id":
"11111111-1111-1111-1111-111111111111",
"routes": [
{
"gateway": "192.168.0.1",
"netmask": "0.0.0.0",
"network": "0.0.0.0"
}
],
"type": "ipv4"
}
],
'services': [
{'address': '1.2.3.4', 'type': 'dns'},
{'address': '2.3.4.5', 'type': 'dns'},
{'address': '1:2:3:4::', 'type': 'dns'},
{'address': '2:3:4:5::', 'type': 'dns'}
]
}
self.netinfo[0]['network']['id'] = (
'00000000-0000-0000-0000-000000000000')
# Add a second NIC
self.netinfo.append(fake_network_cache_model.new_vif({
'type': 'ethernet', 'address': 'aa:aa:aa:aa:aa:ab'}))
address = fake_network_cache_model.new_ip({'address': '192.168.0.2'})
gateway_address = fake_network_cache_model.new_ip(
{'address': '192.168.0.1'})
ipv4_subnet = fake_network_cache_model.new_subnet(
{'cidr': '192.168.0.0/24', 'gateway': gateway_address,
'ips': [address], 'routes': []})
self.netinfo[1]['network']['id'] = (
'11111111-1111-1111-1111-111111111111')
self.netinfo[1]['network']['subnets'][0] = ipv4_subnet
self.netinfo[1]['network']['mtu'] = 1500
network_json = netutils.get_network_metadata(self.netinfo)
self.assertEqual(expected_json, network_json)

View File

@ -177,3 +177,179 @@ def get_injected_network_template(network_info, use_ipv6=None, template=None,
return template.render({'interfaces': nets,
'use_ipv6': ipv6_is_available,
'libvirt_virt_type': libvirt_virt_type})
def get_network_metadata(network_info, use_ipv6=None):
"""Gets a more complete representation of the instance network information.
This data is exposed as network_data.json in the metadata service and
the config drive.
:param network_info: `nova.network.models.NetworkInfo` object describing
the network metadata.
:param use_ipv6: If False, do not return IPv6 template information
even if an IPv6 subnet is present in network_info. Defaults to
nova.netconf.use_ipv6.
"""
if not network_info:
return
if use_ipv6 is None:
use_ipv6 = CONF.use_ipv6
# IPv4 or IPv6 networks
nets = []
# VIFs, physical NICs, or VLANs. Physical NICs will have type 'phy'.
links = []
# Non-network bound services, such as DNS
services = []
ifc_num = -1
net_num = -1
for vif in network_info:
if not vif.get('network') or not vif['network'].get('subnets'):
continue
network = vif['network']
# NOTE(JoshNang) currently, only supports the first IPv4 and first
# IPv6 subnet on network, a limitation that also exists in the
# network template.
subnet_v4 = _get_first_network(network, 4)
subnet_v6 = _get_first_network(network, 6)
ifc_num += 1
link = None
# Get the VIF or physical NIC data
if subnet_v4 or subnet_v6:
link = _get_eth_link(vif, ifc_num)
links.append(link)
# Add IPv4 and IPv6 networks if they exist
if subnet_v4.get('ips'):
net_num += 1
nets.append(_get_nets(vif, subnet_v4, 4, net_num, link['id']))
services += [dns for dns in _get_dns_services(subnet_v4)
if dns not in services]
if (use_ipv6 and subnet_v6) and subnet_v6.get('ips'):
net_num += 1
nets.append(_get_nets(vif, subnet_v6, 6, net_num, link['id']))
services += [dns for dns in _get_dns_services(subnet_v6)
if dns not in services]
return {
"links": links,
"networks": nets,
"services": services
}
def _get_eth_link(vif, ifc_num):
"""Get a VIF or physical NIC representation.
:param vif: Neutron VIF
:param ifc_num: Interface index for generating name if the VIF's
'devname' isn't defined.
:return:
"""
link_id = vif.get('devname')
if not link_id:
link_id = 'interface%d' % ifc_num
# Use 'phy' for physical links. Ethernet can be confusing
if vif.get('type') == 'ethernet':
nic_type = 'phy'
else:
nic_type = vif.get('type')
link = {
'id': link_id,
'vif_id': vif['id'],
'type': nic_type,
'mtu': vif['network'].get('mtu'),
'ethernet_mac_address': vif.get('address'),
}
return link
def _get_nets(vif, subnet, version, net_num, link_id):
"""Get networks for the given VIF and subnet
:param vif: Neutron VIF
:param subnet: Neutron subnet
:param version: IP version as an int, either '4' or '6'
:param net_num: Network index for generating name of each network
:param link_id: Arbitrary identifier for the link the networks are
attached to
"""
if subnet.get_meta('dhcp_server') is not None:
net_info = {
'id': 'network%d' % net_num,
'type': 'ipv%d_dhcp' % version,
'link': link_id,
'network_id': vif['network']['id']
}
return net_info
ip = subnet['ips'][0]
address = ip['address']
if version == 4:
netmask = model.get_netmask(ip, subnet)
elif version == 6:
netmask = str(subnet.as_netaddr().netmask)
net_info = {
'id': 'network%d' % net_num,
'type': 'ipv%d' % version,
'link': link_id,
'ip_address': address,
'netmask': netmask,
'routes': _get_default_route(version, subnet),
'network_id': vif['network']['id']
}
# Add any additional routes beyond the default route
for route in subnet['routes']:
route_addr = netaddr.IPNetwork(route['cidr'])
new_route = {
'network': str(route_addr.network),
'netmask': str(route_addr.netmask),
'gateway': route['gateway']['address']
}
net_info['routes'].append(new_route)
return net_info
def _get_default_route(version, subnet):
"""Get a default route for a network
:param version: IP version as an int, either '4' or '6'
:param subnet: Neutron subnet
"""
if subnet.get('gateway') and subnet['gateway'].get('address'):
gateway = subnet['gateway']['address']
else:
return []
if version == 4:
return [{
'network': '0.0.0.0',
'netmask': '0.0.0.0',
'gateway': gateway
}]
elif version == 6:
return [{
'network': '::',
'netmask': '::',
'gateway': gateway
}]
def _get_dns_services(subnet):
"""Get the DNS servers for the subnet."""
services = []
if not subnet.get('dns'):
return services
return [{'type': 'dns', 'address': ip.get('address')}
for ip in subnet['dns']]