Add get_node_network_data
to Neutron NetworkInterface
Implements `get_node_network_data` network interface method for Neutron networks providing Nova network metadata (AKA network_data.json) collected from Neutron VIF of ironic port objects associated with the node. Co-Authored: Iury Gregory Melo Ferreira <iurygregory@gmail.com> Change-Id: I0fa742110649ed2786f360e1ef43012e77671620 Story: 2006691 Task: 37071
This commit is contained in:
parent
e461e36ee9
commit
c84c6af087
@ -11,6 +11,7 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import ipaddress
|
||||
|
||||
from keystoneauth1 import loading as ks_loading
|
||||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
@ -474,6 +475,160 @@ def remove_neutron_ports(task, params):
|
||||
{'node_uuid': node_uuid})
|
||||
|
||||
|
||||
def _uncidr(cidr, ipv6=False):
|
||||
"""Convert CIDR network representation into network/netmask form
|
||||
|
||||
:param cidr: network in CIDR form
|
||||
:param ipv6: if `True`, consider `cidr` being IPv6
|
||||
:returns: a tuple of network/host number in dotted
|
||||
decimal notation, netmask in dotted decimal notation
|
||||
|
||||
"""
|
||||
net = ipaddress.ip_interface(cidr).network
|
||||
return str(net.network_address), str(net.netmask)
|
||||
|
||||
|
||||
def get_neutron_port_data(port_id, vif_id, client=None, context=None):
|
||||
"""Gather Neutron port and network configuration
|
||||
|
||||
Query Neutron for port and network configuration, return whatever
|
||||
is available.
|
||||
|
||||
:param port_id: ironic port/portgroup ID.
|
||||
:param vif_id: Neutron port ID.
|
||||
:param client: Optional a Neutron client object.
|
||||
:param context: request context
|
||||
:type context: ironic.common.context.RequestContext
|
||||
:raises: NetworkError
|
||||
:returns: a dict holding network configuration information
|
||||
associated with this ironic or Neutron port.
|
||||
"""
|
||||
|
||||
if not client:
|
||||
client = get_client(context=context)
|
||||
|
||||
try:
|
||||
port_config = client.show_port(
|
||||
vif_id, fields=['id', 'name', 'dns_assignment', 'fixed_ips',
|
||||
'mac_address', 'network_id'])
|
||||
|
||||
except neutron_exceptions.NeutronClientException as e:
|
||||
msg = (_('Unable to get port info for %(port_id)s. Error: '
|
||||
'%(err)s') % {'port_id': vif_id, 'err': e})
|
||||
LOG.exception(msg)
|
||||
raise exception.NetworkError(msg)
|
||||
|
||||
LOG.debug('Received port %(port)s data: %(info)s',
|
||||
{'port': vif_id, 'info': port_config})
|
||||
|
||||
port_config = port_config['port']
|
||||
|
||||
port_id = port_config['name'] or port_id
|
||||
|
||||
network_id = port_config.get('network_id')
|
||||
|
||||
try:
|
||||
network_config = client.show_network(
|
||||
network_id, fields=['id', 'mtu', 'subnets'])
|
||||
|
||||
except neutron_exceptions.NeutronClientException as e:
|
||||
msg = (_('Unable to get network info for %(network_id)s. Error: '
|
||||
'%(err)s') % {'network_id': network_id, 'err': e})
|
||||
LOG.exception(msg)
|
||||
raise exception.NetworkError(msg)
|
||||
|
||||
LOG.debug('Received network %(network)s data: %(info)s',
|
||||
{'network': network_id, 'info': network_config})
|
||||
|
||||
network_config = network_config['network']
|
||||
|
||||
subnets_config = {}
|
||||
|
||||
network_data = {
|
||||
'links': [
|
||||
{
|
||||
'id': port_id,
|
||||
'type': 'vif',
|
||||
'ethernet_mac_address': port_config['mac_address'],
|
||||
'vif_id': port_config['id'],
|
||||
'mtu': network_config['mtu']
|
||||
}
|
||||
],
|
||||
'networks': [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
for fixed_ip in port_config.get('fixed_ips', []):
|
||||
subnet_id = fixed_ip['subnet_id']
|
||||
|
||||
try:
|
||||
subnet_config = client.show_subnet(
|
||||
subnet_id, fields=['id', 'name', 'enable_dhcp',
|
||||
'dns_nameservers', 'host_routes',
|
||||
'ip_version', 'gateway_ip', 'cidr'])
|
||||
|
||||
LOG.debug('Received subnet %(subnet)s data: %(info)s',
|
||||
{'subnet': subnet_id, 'info': subnet_config})
|
||||
|
||||
subnets_config[subnet_id] = subnet_config['subnet']
|
||||
|
||||
except neutron_exceptions.NeutronClientException as e:
|
||||
msg = (_('Unable to get subnet info for %(subnet_id)s. Error: '
|
||||
'%(err)s') % {'subnet_id': subnet_id, 'err': e})
|
||||
LOG.exception(msg)
|
||||
raise exception.NetworkError(msg)
|
||||
|
||||
subnet_config = subnets_config[subnet_id]
|
||||
|
||||
subnet_network, netmask = _uncidr(
|
||||
subnet_config['cidr'], subnet_config['ip_version'] == 6)
|
||||
|
||||
network = {
|
||||
'id': fixed_ip['subnet_id'],
|
||||
'network_id': port_config['network_id'],
|
||||
'type': 'ipv%s' % subnet_config['ip_version'],
|
||||
'link': port_id,
|
||||
'ip_address': fixed_ip['ip_address'],
|
||||
'netmask': netmask,
|
||||
'routes': [
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
# TODO(etingof): Adding default route if gateway is present.
|
||||
# This is a hack, Neutron should have given us a route.
|
||||
|
||||
if subnet_config['gateway_ip']:
|
||||
zero_addr = ('::0' if subnet_config['ip_version'] == 6
|
||||
else '0.0.0.0')
|
||||
|
||||
route = {
|
||||
'network': zero_addr,
|
||||
'netmask': zero_addr,
|
||||
'gateway': subnet_config['gateway_ip']
|
||||
}
|
||||
|
||||
network['routes'].append(route)
|
||||
|
||||
for host_config in subnet_config['host_routes']:
|
||||
subnet_network, netmask = _uncidr(
|
||||
host_config['destination'],
|
||||
subnet_config['ip_version'] == 6)
|
||||
|
||||
route = {
|
||||
'network': subnet_network,
|
||||
'netmask': netmask,
|
||||
'gateway': host_config['nexthop']
|
||||
}
|
||||
|
||||
network['routes'].append(route)
|
||||
|
||||
network_data['networks'].append(network)
|
||||
|
||||
return network_data
|
||||
|
||||
|
||||
def get_node_portmap(task):
|
||||
"""Extract the switch port information for the node.
|
||||
|
||||
|
@ -410,7 +410,7 @@ class VIFPortIDMixin(object):
|
||||
or self._get_vif_id_by_port_like_obj(p_obj) or None)
|
||||
|
||||
def get_node_network_data(self, task):
|
||||
"""Return network configuration for node NICs.
|
||||
"""Get network configuration data for node's ports/portgroups.
|
||||
|
||||
Gather L2 and L3 network settings from ironic node `network_data`
|
||||
field. Ironic would eventually pass network configuration to the node
|
||||
@ -633,3 +633,51 @@ class NeutronVIFPortIDMixin(VIFPortIDMixin):
|
||||
# DELETING state.
|
||||
if task.node.provision_state in [states.ACTIVE, states.DELETING]:
|
||||
neutron.unbind_neutron_port(vif_id, context=task.context)
|
||||
|
||||
def get_node_network_data(self, task):
|
||||
"""Get network configuration data for node ports.
|
||||
|
||||
Pull network data from ironic node object if present, otherwise
|
||||
collect it for Neutron VIFs.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:raises: InvalidParameterValue, if the network interface configuration
|
||||
is invalid.
|
||||
:raises: MissingParameterValue, if some parameters are missing.
|
||||
:returns: a dict holding network configuration information adhearing
|
||||
Nova network metadata layout (`network_data.json`).
|
||||
"""
|
||||
# NOTE(etingof): static network data takes precedence
|
||||
network_data = (
|
||||
super(NeutronVIFPortIDMixin, self).get_node_network_data(task))
|
||||
if network_data:
|
||||
return network_data
|
||||
|
||||
node = task.node
|
||||
|
||||
LOG.debug('Gathering network data from ports of node '
|
||||
'%(node)s', {'node': node.uuid})
|
||||
|
||||
network_data = collections.defaultdict(list)
|
||||
|
||||
for port_obj in task.ports:
|
||||
vif_port_id = self.get_current_vif(task, port_obj)
|
||||
|
||||
LOG.debug('Considering node %(node)s port %(port)s, VIF %(vif)s',
|
||||
{'node': node.uuid, 'port': port_obj.uuid,
|
||||
'vif': vif_port_id})
|
||||
|
||||
if not vif_port_id:
|
||||
continue
|
||||
|
||||
port_network_data = neutron.get_neutron_port_data(
|
||||
port_obj.uuid, vif_port_id, context=task.context)
|
||||
|
||||
for field, field_data in port_network_data.items():
|
||||
if field_data:
|
||||
network_data[field].extend(field_data)
|
||||
|
||||
LOG.debug('Collected network data for node %(node)s: %(data)s',
|
||||
{'node': node.uuid, 'data': network_data})
|
||||
|
||||
return network_data
|
||||
|
@ -0,0 +1,33 @@
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"availability_zone_hints": [],
|
||||
"availability_zones": [
|
||||
"nova"
|
||||
],
|
||||
"created_at": "2016-03-08T20:19:41",
|
||||
"dns_domain": "my-domain.org.",
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"ipv4_address_scope": null,
|
||||
"ipv6_address_scope": null,
|
||||
"l2_adjacency": false,
|
||||
"mtu": 1500,
|
||||
"name": "private-network",
|
||||
"port_security_enabled": true,
|
||||
"project_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"qos_policy_id": "6a8454ade84346f59e8d40665f878b2e",
|
||||
"revision_number": 1,
|
||||
"router:external": false,
|
||||
"shared": true,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"tags": ["tag1,tag2"],
|
||||
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||
"updated_at": "2016-03-08T20:19:41",
|
||||
"vlan_transparent": false,
|
||||
"description": "",
|
||||
"is_default": true
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
{
|
||||
"network": {
|
||||
"admin_state_up": true,
|
||||
"availability_zone_hints": [],
|
||||
"availability_zones": [
|
||||
"nova"
|
||||
],
|
||||
"created_at": "2016-03-08T20:19:41",
|
||||
"dns_domain": "my-domain.org.",
|
||||
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22",
|
||||
"ipv4_address_scope": null,
|
||||
"ipv6_address_scope": null,
|
||||
"l2_adjacency": false,
|
||||
"mtu": 1500,
|
||||
"name": "private-network",
|
||||
"port_security_enabled": true,
|
||||
"project_id": "5199666e520f4aed823710aec37cfd38",
|
||||
"qos_policy_id": "6a8454ade84346f59e8d40665f878b2e",
|
||||
"revision_number": 1,
|
||||
"router:external": false,
|
||||
"shared": true,
|
||||
"status": "ACTIVE",
|
||||
"subnets": [
|
||||
"54d6f61d-db07-451c-9ab3-b9609b6b6f0b"
|
||||
],
|
||||
"tags": ["tag1,tag2"],
|
||||
"tenant_id": "5199666e520f4aed823710aec37cfd38",
|
||||
"updated_at": "2016-03-08T20:19:41",
|
||||
"vlan_transparent": false,
|
||||
"description": "",
|
||||
"is_default": true
|
||||
}
|
||||
}
|
59
ironic/tests/unit/common/json_samples/neutron_port_show.json
Normal file
59
ironic/tests/unit/common/json_samples/neutron_port_show.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"port": {
|
||||
"admin_state_up": true,
|
||||
"allowed_address_pairs": [],
|
||||
"binding:host_id": "devstack",
|
||||
"binding:profile": {},
|
||||
"binding:vif_details": {
|
||||
"ovs_hybrid_plug": true,
|
||||
"port_filter": true
|
||||
},
|
||||
"binding:vif_type": "ovs",
|
||||
"binding:vnic_type": "normal",
|
||||
"created_at": "2016-03-08T20:19:41",
|
||||
"data_plane_status": "ACTIVE",
|
||||
"description": "",
|
||||
"device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e",
|
||||
"device_owner": "network:router_interface",
|
||||
"dns_assignment": {
|
||||
"hostname": "myport",
|
||||
"ip_address": "10.0.0.2",
|
||||
"fqdn": "myport.my-domain.org"
|
||||
},
|
||||
"dns_domain": "my-domain.org.",
|
||||
"dns_name": "myport",
|
||||
"extra_dhcp_opts": [
|
||||
{
|
||||
"opt_value": "pxelinux.0",
|
||||
"ip_version": 4,
|
||||
"opt_name": "bootfile-name"
|
||||
}
|
||||
],
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "10.0.0.2",
|
||||
"subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2"
|
||||
}
|
||||
],
|
||||
"id": "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2",
|
||||
"ip_allocation": "immediate",
|
||||
"mac_address": "fa:16:3e:23:fd:d7",
|
||||
"mac_learning_enabled": false,
|
||||
"name": "",
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"port_security_enabled": false,
|
||||
"project_id": "7e02058126cc4950b75f9970368ba177",
|
||||
"revision_number": 1,
|
||||
"security_groups": [],
|
||||
"status": "ACTIVE",
|
||||
"tags": ["tag1,tag2"],
|
||||
"tenant_id": "7e02058126cc4950b75f9970368ba177",
|
||||
"updated_at": "2016-03-08T20:19:41",
|
||||
"qos_policy_id": "29d5e02e-d5ab-4929-bee4-4a9fc12e22ae",
|
||||
"resource_request": {
|
||||
"required": ["CUSTOM_PHYSNET_PUBLIC", "CUSTOM_VNIC_TYPE_NORMAL"],
|
||||
"resources": {"NET_BW_EGR_KILOBIT_PER_SEC": 1000}
|
||||
},
|
||||
"uplink_status_propagation": false
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
{
|
||||
"port": {
|
||||
"admin_state_up": true,
|
||||
"allowed_address_pairs": [],
|
||||
"binding:host_id": "devstack",
|
||||
"binding:profile": {},
|
||||
"binding:vif_details": {
|
||||
"ovs_hybrid_plug": true,
|
||||
"port_filter": true
|
||||
},
|
||||
"binding:vif_type": "ovs",
|
||||
"binding:vnic_type": "normal",
|
||||
"created_at": "2016-03-08T20:19:41",
|
||||
"data_plane_status": "ACTIVE",
|
||||
"description": "",
|
||||
"device_id": "5e3898d7-11be-483e-9732-b2f5eccd2b2e",
|
||||
"device_owner": "network:router_interface",
|
||||
"dns_assignment": {
|
||||
"hostname": "myport",
|
||||
"ip_address": "fd00:203:0:113::2",
|
||||
"fqdn": "myport.my-domain.org"
|
||||
},
|
||||
"dns_domain": "my-domain.org.",
|
||||
"dns_name": "myport",
|
||||
"extra_dhcp_opts": [
|
||||
{
|
||||
"opt_value": "pxelinux.0",
|
||||
"ip_version": 6,
|
||||
"opt_name": "bootfile-name"
|
||||
}
|
||||
],
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "fd00:203:0:113::2",
|
||||
"subnet_id": "906e685a-b964-4d58-9939-9cf3af197c67"
|
||||
}
|
||||
],
|
||||
"id": "96d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb8",
|
||||
"ip_allocation": "immediate",
|
||||
"mac_address": "52:54:00:4f:ef:b7",
|
||||
"mac_learning_enabled": false,
|
||||
"name": "",
|
||||
"network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
|
||||
"port_security_enabled": false,
|
||||
"project_id": "7e02058126cc4950b75f9970368ba177",
|
||||
"revision_number": 1,
|
||||
"security_groups": [],
|
||||
"status": "ACTIVE",
|
||||
"tags": ["tag1,tag2"],
|
||||
"tenant_id": "7e02058126cc4950b75f9970368ba177",
|
||||
"updated_at": "2016-03-08T20:19:41",
|
||||
"qos_policy_id": "29d5e02e-d5ab-4929-bee4-4a9fc12e22ae",
|
||||
"resource_request": {
|
||||
"required": ["CUSTOM_PHYSNET_PUBLIC", "CUSTOM_VNIC_TYPE_NORMAL"],
|
||||
"resources": {"NET_BW_EGR_KILOBIT_PER_SEC": 1000}
|
||||
},
|
||||
"uplink_status_propagation": false
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"subnet": {
|
||||
"name": "private-subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
"segment_id": null,
|
||||
"project_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"dns_nameservers": [],
|
||||
"dns_publish_fixed_ip": false,
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "10.0.0.2",
|
||||
"end": "10.0.0.254"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 4,
|
||||
"gateway_ip": "10.0.0.1",
|
||||
"cidr": "10.0.0.0/24",
|
||||
"id": "08eae331-0402-425a-923c-34f7cfe39c1b",
|
||||
"created_at": "2016-10-10T14:35:34Z",
|
||||
"description": "",
|
||||
"ipv6_address_mode": null,
|
||||
"ipv6_ra_mode": null,
|
||||
"revision_number": 2,
|
||||
"service_types": [],
|
||||
"subnetpool_id": null,
|
||||
"tags": ["tag1,tag2"],
|
||||
"updated_at": "2016-10-10T14:35:34Z"
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"subnet": {
|
||||
"name": "private-subnet",
|
||||
"enable_dhcp": true,
|
||||
"network_id": "db193ab3-96e3-4cb3-8fc5-05f4296d0324",
|
||||
"segment_id": null,
|
||||
"project_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"tenant_id": "26a7980765d0414dbc1fc1f88cdb7e6e",
|
||||
"dns_nameservers": [],
|
||||
"dns_publish_fixed_ip": false,
|
||||
"allocation_pools": [
|
||||
{
|
||||
"start": "fd00:203:0:113::2",
|
||||
"end": "fd00:203:0:113:ffff:ffff:ffff:ffff"
|
||||
}
|
||||
],
|
||||
"host_routes": [],
|
||||
"ip_version": 6,
|
||||
"gateway_ip": "fd00:203:0:113::1",
|
||||
"cidr": "fd00:203:0:113::/64",
|
||||
"id": "08eae331-0402-425a-923c-34f7cfe39c1b",
|
||||
"created_at": "2016-10-10T14:35:34Z",
|
||||
"description": "",
|
||||
"ipv6_address_mode": "slaac",
|
||||
"ipv6_ra_mode": null,
|
||||
"revision_number": 2,
|
||||
"service_types": [],
|
||||
"subnetpool_id": null,
|
||||
"tags": ["tag1,tag2"],
|
||||
"updated_at": "2016-10-10T14:35:34Z"
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
@ -270,6 +272,30 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
patcher.start()
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
port_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_port_show.json')
|
||||
with open(port_show_file, 'rb') as fl:
|
||||
self.port_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_port.return_value = self.port_data
|
||||
|
||||
network_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_network_show.json')
|
||||
with open(network_show_file, 'rb') as fl:
|
||||
self.network_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_network.return_value = self.network_data
|
||||
|
||||
subnet_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_subnet_show.json')
|
||||
with open(subnet_show_file, 'rb') as fl:
|
||||
self.subnet_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_subnet.return_value = self.subnet_data
|
||||
|
||||
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
|
||||
def _test_add_ports_to_network(self, update_mock, is_client_id,
|
||||
security_groups=None,
|
||||
@ -667,6 +693,103 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
self.client_mock.delete_port.assert_called_once_with(
|
||||
self.neutron_port['id'])
|
||||
|
||||
def test__uncidr_ipv4(self):
|
||||
network, netmask = neutron._uncidr('10.0.0.0/24')
|
||||
self.assertEqual('10.0.0.0', network)
|
||||
self.assertEqual('255.255.255.0', netmask)
|
||||
|
||||
def test__uncidr_ipv6(self):
|
||||
network, netmask = neutron._uncidr('::1/64', ipv6=True)
|
||||
self.assertEqual('::', network)
|
||||
self.assertEqual('ffff:ffff:ffff:ffff::', netmask)
|
||||
|
||||
def test_get_neutron_port_data(self):
|
||||
|
||||
network_data = neutron.get_neutron_port_data('port0', 'vif0')
|
||||
|
||||
expected_port = {
|
||||
'id': 'port0',
|
||||
'type': 'vif',
|
||||
'ethernet_mac_address': 'fa:16:3e:23:fd:d7',
|
||||
'vif_id': '46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2',
|
||||
'mtu': 1500
|
||||
}
|
||||
|
||||
self.assertEqual(expected_port, network_data['links'][0])
|
||||
|
||||
expected_network = {
|
||||
'id': 'a0304c3a-4f08-4c43-88af-d796509c97d2',
|
||||
'network_id': 'a87cc70a-3e15-4acf-8205-9b711a3531b7',
|
||||
'type': 'ipv4',
|
||||
'link': 'port0',
|
||||
'ip_address': '10.0.0.2',
|
||||
'netmask': '255.255.255.0',
|
||||
'routes': [
|
||||
{'gateway': '10.0.0.1',
|
||||
'netmask': '0.0.0.0',
|
||||
'network': '0.0.0.0'}
|
||||
]
|
||||
}
|
||||
|
||||
self.assertEqual(expected_network, network_data['networks'][0])
|
||||
|
||||
def load_ipv6_files(self):
|
||||
port_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_port_show_ipv6.json')
|
||||
with open(port_show_file, 'rb') as fl:
|
||||
self.port_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_port.return_value = self.port_data
|
||||
|
||||
network_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_network_show_ipv6.json')
|
||||
with open(network_show_file, 'rb') as fl:
|
||||
self.network_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_network.return_value = self.network_data
|
||||
|
||||
subnet_show_file = os.path.join(
|
||||
os.path.dirname(__file__), 'json_samples',
|
||||
'neutron_subnet_show_ipv6.json')
|
||||
with open(subnet_show_file, 'rb') as fl:
|
||||
self.subnet_data = json.load(fl)
|
||||
|
||||
self.client_mock.show_subnet.return_value = self.subnet_data
|
||||
|
||||
def test_get_neutron_port_data_ipv6(self):
|
||||
self.load_ipv6_files()
|
||||
|
||||
network_data = neutron.get_neutron_port_data('port1', 'vif1')
|
||||
|
||||
print(network_data)
|
||||
expected_port = {
|
||||
'id': 'port1',
|
||||
'type': 'vif',
|
||||
'ethernet_mac_address': '52:54:00:4f:ef:b7',
|
||||
'vif_id': '96d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb8',
|
||||
'mtu': 1500
|
||||
}
|
||||
|
||||
self.assertEqual(expected_port, network_data['links'][0])
|
||||
|
||||
expected_network = {
|
||||
'id': '906e685a-b964-4d58-9939-9cf3af197c67',
|
||||
'network_id': 'a87cc70a-3e15-4acf-8205-9b711a3531b7',
|
||||
'type': 'ipv6',
|
||||
'link': 'port1',
|
||||
'ip_address': 'fd00:203:0:113::2',
|
||||
'netmask': 'ffff:ffff:ffff:ffff::',
|
||||
'routes': [
|
||||
{'gateway': 'fd00:203:0:113::1',
|
||||
'netmask': '::0',
|
||||
'network': '::0'}
|
||||
]
|
||||
}
|
||||
|
||||
self.assertEqual(expected_network, network_data['networks'][0])
|
||||
|
||||
def test_get_node_portmap(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
portmap = neutron.get_node_portmap(task)
|
||||
|
@ -339,7 +339,10 @@ class TestFlatInterface(db_base.DbTestCase):
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
self.interface.validate_inspection, task)
|
||||
|
||||
def test_get_node_network_data(self):
|
||||
@mock.patch.object(neutron, 'get_neutron_port_data', autospec=True)
|
||||
def test_get_node_network_data(self, mock_gnpd):
|
||||
mock_gnpd.return_value = {}
|
||||
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
network_data = self.interface.get_node_network_data(task)
|
||||
|
||||
|
@ -699,13 +699,15 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
||||
self.node.save()
|
||||
self._test_configure_tenant_networks(is_client_id=True)
|
||||
|
||||
@mock.patch.object(neutron_common, 'get_neutron_port_data', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_client', autospec=True)
|
||||
@mock.patch.object(neutron_common, 'get_local_group_information',
|
||||
autospec=True)
|
||||
def test_configure_tenant_networks_with_portgroups(
|
||||
self, glgi_mock, client_mock, update_mock, wait_agent_mock):
|
||||
self, glgi_mock, client_mock, update_mock, wait_agent_mock,
|
||||
port_data_mock):
|
||||
pg = utils.create_test_portgroup(
|
||||
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
|
||||
extra={'vif_port_id': uuidutils.generate_uuid()})
|
||||
@ -860,7 +862,10 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
|
||||
self.assertRaises(exception.UnsupportedDriverExtension,
|
||||
self.interface.validate_inspection, task)
|
||||
|
||||
def test_get_node_network_data(self):
|
||||
@mock.patch.object(neutron_common, 'get_neutron_port_data', autospec=True)
|
||||
def test_get_node_network_data(self, mock_gnpd):
|
||||
mock_gnpd.return_value = {}
|
||||
|
||||
with task_manager.acquire(self.context, self.node.id) as task:
|
||||
network_data = self.interface.get_node_network_data(task)
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds `network_data` property to the node, a dictionary that represents the
|
||||
node static network configuration. The Ironic API performs formal JSON
|
||||
validation of node `network_data` content against user-supplied JSON schema
|
||||
at driver validation step.
|
Loading…
Reference in New Issue
Block a user