Add get_network_details_v2 in BaseOpenStackService
Partially-Implements: blueprint json-network-config Change-Id: Ia2a31518f87f34c234988fc512f1698617efdb10 Co-Authored-By: Adrian Vladu <avladu@cloudbasesolutions.com>
This commit is contained in:
parent
6782252536
commit
1cdf43ce23
@ -16,14 +16,28 @@
|
||||
import json
|
||||
import posixpath
|
||||
|
||||
import netaddr
|
||||
from oslo_log import log as oslo_logging
|
||||
|
||||
from cloudbaseinit import conf as cloudbaseinit_conf
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.models import network as network_model
|
||||
from cloudbaseinit.utils import debiface
|
||||
from cloudbaseinit.utils import encoding
|
||||
from cloudbaseinit.utils import x509constants
|
||||
|
||||
NETWORK_LINK_TYPE_PHYSICAL = "phy"
|
||||
NETWORK_LINK_TYPE_BOND = "bond"
|
||||
NETWORK_LINK_TYPE_VLAN = "vlan"
|
||||
|
||||
NETWORK_TYPE_IPV4 = "ipv4"
|
||||
NETWORK_TYPE_IPV4_DHCP = "ipv4_dhcp"
|
||||
NETWORK_TYPE_IPV6 = "ipv6"
|
||||
NETWORK_TYPE_IPV6_DHCP = "ipv6_dhcp"
|
||||
|
||||
NETWORK_SERVICE_TYPE_DNS = "dns"
|
||||
|
||||
CONF = cloudbaseinit_conf.CONF
|
||||
LOG = oslo_logging.getLogger(__name__)
|
||||
|
||||
@ -40,13 +54,19 @@ class BaseOpenStackService(base.BaseMetadataService):
|
||||
posixpath.join('openstack', 'latest', 'user_data'))
|
||||
return self._get_cache_data(path)
|
||||
|
||||
def _get_meta_data(self, version='latest'):
|
||||
def _get_openstack_json_data(self, version, file_name):
|
||||
path = posixpath.normpath(
|
||||
posixpath.join('openstack', version, 'meta_data.json'))
|
||||
posixpath.join('openstack', version, file_name))
|
||||
data = self._get_cache_data(path, decode=True)
|
||||
if data:
|
||||
return json.loads(data)
|
||||
|
||||
def _get_meta_data(self, version='latest'):
|
||||
return self._get_openstack_json_data(version, 'meta_data.json')
|
||||
|
||||
def _get_network_data(self, version='latest'):
|
||||
return self._get_openstack_json_data(version, 'network_data.json')
|
||||
|
||||
def get_instance_id(self):
|
||||
return self._get_meta_data().get('uuid')
|
||||
|
||||
@ -81,6 +101,158 @@ class BaseOpenStackService(base.BaseMetadataService):
|
||||
|
||||
return debiface.parse(content)
|
||||
|
||||
@staticmethod
|
||||
def _ip_netmask_to_cidr(ip_address, netmask):
|
||||
if netmask is None:
|
||||
return ip_address
|
||||
prefix_len = netaddr.IPNetwork(
|
||||
u"%s/%s" % (ip_address, netmask)).prefixlen
|
||||
return u"%s/%s" % (ip_address, prefix_len)
|
||||
|
||||
@staticmethod
|
||||
def _parse_network_data_links(links_data):
|
||||
links = []
|
||||
for link_data in links_data:
|
||||
link_id = link_data.get("id")
|
||||
mac = link_data.get("ethernet_mac_address")
|
||||
mtu = link_data.get("mtu")
|
||||
openstack_link_type = link_data.get("type")
|
||||
|
||||
bond = None
|
||||
vlan_id = None
|
||||
vlan_link = None
|
||||
if openstack_link_type == NETWORK_LINK_TYPE_BOND:
|
||||
link_type = network_model.LINK_TYPE_BOND
|
||||
bond_links = link_data.get("bond_links")
|
||||
bond_mode = link_data.get("bond_mode")
|
||||
bond_xmit_hash_policy = link_data.get("bond_xmit_hash_policy")
|
||||
|
||||
if bond_mode not in network_model.AVAILABLE_BOND_TYPES:
|
||||
raise exception.CloudbaseInitException(
|
||||
"Unsupported bond mode: %s" % bond_mode)
|
||||
|
||||
if (bond_xmit_hash_policy is not None and
|
||||
bond_xmit_hash_policy not in
|
||||
network_model.AVAILABLE_BOND_LB_ALGORITHMS):
|
||||
raise exception.CloudbaseInitException(
|
||||
"Unsupported bond hash policy: %s" %
|
||||
bond_xmit_hash_policy)
|
||||
|
||||
bond = network_model.Bond(
|
||||
members=bond_links,
|
||||
type=bond_mode,
|
||||
lb_algorithm=bond_xmit_hash_policy,
|
||||
lacp_rate=None,
|
||||
)
|
||||
elif openstack_link_type == NETWORK_LINK_TYPE_VLAN:
|
||||
link_type = network_model.LINK_TYPE_VLAN
|
||||
vlan_id = link_data.get("vlan_id")
|
||||
vlan_link = link_data.get("vlan_link")
|
||||
vlan_mac_address = link_data.get("vlan_mac_address")
|
||||
if vlan_mac_address is not None:
|
||||
mac = vlan_mac_address
|
||||
else:
|
||||
# Any other link type is considered physical
|
||||
link_type = network_model.LINK_TYPE_PHYSICAL
|
||||
|
||||
link = network_model.Link(
|
||||
id=link_id,
|
||||
name=link_id,
|
||||
type=link_type,
|
||||
enabled=True,
|
||||
mac_address=mac,
|
||||
mtu=mtu,
|
||||
bond=bond,
|
||||
vlan_id=vlan_id,
|
||||
vlan_link=vlan_link)
|
||||
links.append(link)
|
||||
|
||||
return links
|
||||
|
||||
@staticmethod
|
||||
def _parse_dns_data(services_data):
|
||||
dns_nameservers = []
|
||||
for service_data in services_data:
|
||||
service_type = service_data.get("type")
|
||||
if service_type != NETWORK_SERVICE_TYPE_DNS:
|
||||
LOG.warn("Skipping unsupported service type: %s", service_type)
|
||||
continue
|
||||
|
||||
address = service_data.get("address")
|
||||
if address is not None:
|
||||
dns_nameservers.append(address)
|
||||
|
||||
return dns_nameservers
|
||||
|
||||
@staticmethod
|
||||
def _parse_network_data_networks(networks_data):
|
||||
networks = []
|
||||
for network_data in networks_data:
|
||||
network_type = network_data.get("type")
|
||||
if network_type not in [NETWORK_TYPE_IPV4, NETWORK_TYPE_IPV6]:
|
||||
continue
|
||||
|
||||
link_id = network_data.get("link")
|
||||
ip_address = network_data.get("ip_address")
|
||||
netmask = network_data.get("netmask")
|
||||
address_cidr = BaseOpenStackService._ip_netmask_to_cidr(
|
||||
ip_address, netmask)
|
||||
|
||||
routes = []
|
||||
for route_data in network_data.get("routes", []):
|
||||
gateway = route_data.get("gateway")
|
||||
network = route_data.get("network")
|
||||
netmask = route_data.get("netmask")
|
||||
network_cidr = BaseOpenStackService._ip_netmask_to_cidr(
|
||||
network, netmask)
|
||||
|
||||
route = network_model.Route(
|
||||
network_cidr=network_cidr,
|
||||
gateway=gateway
|
||||
)
|
||||
routes.append(route)
|
||||
|
||||
dns_nameservers = BaseOpenStackService._parse_dns_data(
|
||||
network_data.get("services", []))
|
||||
|
||||
network = network_model.Network(
|
||||
link=link_id,
|
||||
address_cidr=address_cidr,
|
||||
dns_nameservers=dns_nameservers,
|
||||
routes=routes
|
||||
)
|
||||
networks.append(network)
|
||||
|
||||
return networks
|
||||
|
||||
@staticmethod
|
||||
def _parse_network_data_services(services_data):
|
||||
services = []
|
||||
dns_nameservers = BaseOpenStackService._parse_dns_data(services_data)
|
||||
if len(dns_nameservers):
|
||||
service = network_model.NameServerService(
|
||||
addresses=dns_nameservers,
|
||||
search=None
|
||||
)
|
||||
services.append(service)
|
||||
return services
|
||||
|
||||
def get_network_details_v2(self):
|
||||
network_data = self._get_network_data()
|
||||
|
||||
links = self._parse_network_data_links(
|
||||
network_data.get("links", []))
|
||||
networks = self._parse_network_data_networks(
|
||||
network_data.get("networks", []))
|
||||
services = self._parse_network_data_services(
|
||||
network_data.get("services", []))
|
||||
|
||||
return network_model.NetworkDetailsV2(
|
||||
links=links,
|
||||
networks=networks,
|
||||
services=services
|
||||
)
|
||||
|
||||
def get_admin_password(self):
|
||||
meta_data = self._get_meta_data()
|
||||
meta = meta_data.get('meta')
|
||||
|
@ -17,12 +17,15 @@ import functools
|
||||
import posixpath
|
||||
import unittest
|
||||
|
||||
import netaddr
|
||||
|
||||
try:
|
||||
import unittest.mock as mock
|
||||
except ImportError:
|
||||
import mock
|
||||
|
||||
from cloudbaseinit import conf as cloudbaseinit_conf
|
||||
from cloudbaseinit import exception
|
||||
from cloudbaseinit.metadata.services import base
|
||||
from cloudbaseinit.metadata.services import baseopenstackservice
|
||||
from cloudbaseinit.models import network as network_model
|
||||
@ -265,3 +268,223 @@ class TestBaseOpenStackService(unittest.TestCase):
|
||||
|
||||
def test_get_network_details(self):
|
||||
self._partial_test_get_network_details()
|
||||
|
||||
@staticmethod
|
||||
def _get_network_data():
|
||||
return {
|
||||
"links": [{
|
||||
"ethernet_mac_address": mock.sentinel.link_mac1,
|
||||
"type": baseopenstackservice.NETWORK_LINK_TYPE_PHYSICAL,
|
||||
"id": mock.sentinel.link_id1,
|
||||
"mtu": mock.sentinel.link_mtu1,
|
||||
}, {
|
||||
"ethernet_mac_address": mock.sentinel.link_mac2,
|
||||
"type": mock.sentinel.another_link_type,
|
||||
"id": mock.sentinel.link_id2,
|
||||
"mtu": mock.sentinel.link_mtu2,
|
||||
}, {
|
||||
"bond_miimon": mock.sentinel.bond_miimon1,
|
||||
"bond_xmit_hash_policy": mock.sentinel.bond_lb_algo1,
|
||||
"ethernet_mac_address": mock.sentinel.bond_mac1,
|
||||
"mtu": mock.sentinel.bond_mtu1,
|
||||
"bond_mode": mock.sentinel.bond_type1,
|
||||
"bond_links": [
|
||||
mock.sentinel.link_id1,
|
||||
mock.sentinel.link_id2,
|
||||
],
|
||||
"type": baseopenstackservice.NETWORK_LINK_TYPE_BOND,
|
||||
"id": mock.sentinel.bond_id1,
|
||||
}, {
|
||||
"id": mock.sentinel.vlan_link_id1,
|
||||
"type": baseopenstackservice.NETWORK_LINK_TYPE_VLAN,
|
||||
"vlan_link": mock.sentinel.bond_id1,
|
||||
"vlan_id": mock.sentinel.vlan_id1,
|
||||
"mtu": mock.sentinel.vlan_mtu1,
|
||||
"ethernet_mac_address": mock.sentinel.vlan_mac1,
|
||||
}],
|
||||
"networks": [{
|
||||
"id": mock.sentinel.network_id1,
|
||||
"network_id": mock.sentinel.network_openstack_id1,
|
||||
"link": mock.sentinel.bond_id1,
|
||||
"type": baseopenstackservice.NETWORK_TYPE_IPV4_DHCP,
|
||||
}, {
|
||||
"id": mock.sentinel.network_id2,
|
||||
"type": baseopenstackservice.NETWORK_TYPE_IPV4,
|
||||
"link": mock.sentinel.bond_id1,
|
||||
"ip_address": mock.sentinel.ip_address1,
|
||||
"netmask": mock.sentinel.netmask1,
|
||||
"services": [{
|
||||
"type": baseopenstackservice.NETWORK_SERVICE_TYPE_DNS,
|
||||
"address": mock.sentinel.dns1,
|
||||
}, {
|
||||
"type": baseopenstackservice.NETWORK_SERVICE_TYPE_DNS,
|
||||
"address": mock.sentinel.dns2
|
||||
}],
|
||||
"routes": [{
|
||||
"network": mock.sentinel.route_network1,
|
||||
"netmask": mock.sentinel.route_netmask1,
|
||||
"gateway": mock.sentinel.route_gateway1,
|
||||
}, {
|
||||
"network": mock.sentinel.route_network2,
|
||||
"netmask": mock.sentinel.route_netmask2,
|
||||
"gateway": mock.sentinel.route_gateway2,
|
||||
}],
|
||||
"network_id": mock.sentinel.network_openstack_id2
|
||||
}, {
|
||||
"id": mock.sentinel.network_id3,
|
||||
"type": baseopenstackservice.NETWORK_TYPE_IPV6,
|
||||
"link": mock.sentinel.bond_id1,
|
||||
"ip_address": mock.sentinel.ip_address_ipv61,
|
||||
"routes": [{
|
||||
"network": mock.sentinel.route_network_ipv61,
|
||||
"gateway": mock.sentinel.route_gateway_ipv61,
|
||||
}],
|
||||
"network_id": mock.sentinel.network_openstack_id3
|
||||
}],
|
||||
"services": [{
|
||||
"type": baseopenstackservice.NETWORK_SERVICE_TYPE_DNS,
|
||||
"address": mock.sentinel.dns3,
|
||||
}, {
|
||||
"type": baseopenstackservice.NETWORK_SERVICE_TYPE_DNS,
|
||||
"address": mock.sentinel.dns4
|
||||
}],
|
||||
}
|
||||
|
||||
@mock.patch(MODPATH + ".BaseOpenStackService._get_network_data")
|
||||
def _test_get_network_details_v2(self, mock_get_network_data,
|
||||
invalid_bond_type=False,
|
||||
invalid_bond_lb_algo=False):
|
||||
mock.sentinel.ip_address1 = "10.0.0.1"
|
||||
mock.sentinel.netmask1 = "255.255.255.0"
|
||||
mock.sentinel.route_network1 = "172.16.0.0"
|
||||
mock.sentinel.route_netmask1 = "255.255.0.0"
|
||||
mock.sentinel.route_gateway1 = "172.16.1.1"
|
||||
mock.sentinel.route_network2 = "0.0.0.0"
|
||||
mock.sentinel.route_netmask2 = "0.0.0.0"
|
||||
mock.sentinel.route_gateway2 = "10.0.0.254"
|
||||
mock.sentinel.ip_address_ipv61 = "2001:cdba::3257:9652/24"
|
||||
mock.sentinel.route_network_ipv61 = "::/0"
|
||||
mock.sentinel.route_gateway_ipv61 = "fd00::1"
|
||||
|
||||
if invalid_bond_type:
|
||||
mock.sentinel.bond_type1 = "invalid bond type"
|
||||
else:
|
||||
mock.sentinel.bond_type1 = network_model.BOND_TYPE_ACTIVE_BACKUP
|
||||
|
||||
if invalid_bond_lb_algo:
|
||||
mock.sentinel.bond_lb_algo1 = "invalid lb algorithm"
|
||||
else:
|
||||
mock.sentinel.bond_lb_algo1 = network_model.BOND_LB_ALGO_L2
|
||||
|
||||
network_data = self._get_network_data()
|
||||
|
||||
mock_get_network_data.return_value = network_data
|
||||
|
||||
if invalid_bond_type or invalid_bond_lb_algo:
|
||||
with self.assertRaises(exception.CloudbaseInitException):
|
||||
self._service.get_network_details_v2()
|
||||
return
|
||||
|
||||
network_details = self._service.get_network_details_v2()
|
||||
|
||||
self.assertEqual(
|
||||
len(network_data["links"]), len(network_details.links))
|
||||
|
||||
self.assertEqual(1, len([
|
||||
l for l in network_details.links if
|
||||
l.type == network_model.LINK_TYPE_PHYSICAL and
|
||||
l.id == mock.sentinel.link_id1 and
|
||||
l.name == mock.sentinel.link_id1 and
|
||||
l.mac_address == mock.sentinel.link_mac1 and
|
||||
l.mtu == mock.sentinel.link_mtu1]))
|
||||
|
||||
self.assertEqual(1, len([
|
||||
l for l in network_details.links if
|
||||
l.type == network_model.LINK_TYPE_PHYSICAL and
|
||||
l.id == mock.sentinel.link_id2 and
|
||||
l.name == mock.sentinel.link_id2 and
|
||||
l.mac_address == mock.sentinel.link_mac2 and
|
||||
l.mtu == mock.sentinel.link_mtu2]))
|
||||
|
||||
self.assertEqual(1, len([
|
||||
l for l in network_details.links if
|
||||
l.type == network_model.LINK_TYPE_BOND and
|
||||
l.id == mock.sentinel.bond_id1 and
|
||||
l.name == mock.sentinel.bond_id1 and
|
||||
l.mtu == mock.sentinel.bond_mtu1 and
|
||||
l.mac_address == mock.sentinel.bond_mac1 and
|
||||
l.vlan_link is None and
|
||||
l.vlan_id is None and
|
||||
l.bond.type == network_model.BOND_TYPE_ACTIVE_BACKUP and
|
||||
l.bond.members == [
|
||||
mock.sentinel.link_id1, mock.sentinel.link_id2] and
|
||||
l.bond.lb_algorithm == network_model.BOND_LB_ALGO_L2 and
|
||||
l.bond.lacp_rate is None]))
|
||||
|
||||
self.assertEqual(1, len([
|
||||
l for l in network_details.links if
|
||||
l.type == network_model.LINK_TYPE_VLAN and
|
||||
l.id == mock.sentinel.vlan_link_id1 and
|
||||
l.name == mock.sentinel.vlan_link_id1 and
|
||||
l.mac_address == mock.sentinel.vlan_mac1 and
|
||||
l.mtu == mock.sentinel.vlan_mtu1 and
|
||||
l.vlan_link == mock.sentinel.bond_id1 and
|
||||
l.vlan_id == mock.sentinel.vlan_id1]))
|
||||
|
||||
self.assertEqual(
|
||||
len([n for n in network_data["networks"]
|
||||
if n["type"] in [
|
||||
baseopenstackservice.NETWORK_TYPE_IPV4,
|
||||
baseopenstackservice.NETWORK_TYPE_IPV6]]),
|
||||
len(network_details.networks))
|
||||
|
||||
def _get_cidr_address(ip_address, netmask):
|
||||
prefix_len = netaddr.IPNetwork(
|
||||
u"%s/%s" % (ip_address, netmask)).prefixlen
|
||||
return u"%s/%s" % (ip_address, prefix_len)
|
||||
|
||||
address_cidr = _get_cidr_address(
|
||||
mock.sentinel.ip_address1, mock.sentinel.netmask1)
|
||||
|
||||
network = [
|
||||
n for n in network_details.networks
|
||||
if n.address_cidr == address_cidr and
|
||||
n.dns_nameservers == [mock.sentinel.dns1, mock.sentinel.dns2] and
|
||||
n.link == mock.sentinel.bond_id1]
|
||||
self.assertEqual(1, len(network))
|
||||
|
||||
network_cidr1 = _get_cidr_address(
|
||||
mock.sentinel.route_network1, mock.sentinel.route_netmask1)
|
||||
|
||||
network_cidr2 = _get_cidr_address(
|
||||
mock.sentinel.route_network2, mock.sentinel.route_netmask2)
|
||||
|
||||
self.assertEqual([
|
||||
network_model.Route(
|
||||
network_cidr=network_cidr1,
|
||||
gateway=mock.sentinel.route_gateway1),
|
||||
network_model.Route(
|
||||
network_cidr=network_cidr2,
|
||||
gateway=mock.sentinel.route_gateway2)],
|
||||
network[0].routes)
|
||||
|
||||
network_ipv6 = [
|
||||
n for n in network_details.networks
|
||||
if n.address_cidr == mock.sentinel.ip_address_ipv61 and
|
||||
n.link == mock.sentinel.bond_id1]
|
||||
self.assertEqual(1, len(network_ipv6))
|
||||
|
||||
self.assertEqual(
|
||||
[network_model.NameServerService(
|
||||
addresses=[mock.sentinel.dns3, mock.sentinel.dns4],
|
||||
search=None)],
|
||||
network_details.services)
|
||||
|
||||
def test_get_network_details_v2(self):
|
||||
self._test_get_network_details_v2()
|
||||
|
||||
def test_get_network_details_v2_invalid_bond_type(self):
|
||||
self._test_get_network_details_v2(invalid_bond_type=True)
|
||||
|
||||
def test_get_network_details_v2_invalid_bond_lb_algo(self):
|
||||
self._test_get_network_details_v2(invalid_bond_lb_algo=True)
|
||||
|
Loading…
x
Reference in New Issue
Block a user