Merge "Generate necessary network metadata for ironic port groups"

This commit is contained in:
Jenkins
2017-01-20 19:32:39 +00:00
committed by Gerrit Code Review
4 changed files with 138 additions and 11 deletions
+64 -2
View File
@@ -1707,13 +1707,75 @@ class IronicDriverGenerateConfigDriveTestCase(test.NoDBTestCase):
request_context=None)
@mock.patch.object(FAKE_CLIENT.node, 'list_ports')
def test_generate_network_metadata_ports_only(
self, mock_ports, mock_cd_builder, mock_instance_meta):
@mock.patch.object(FAKE_CLIENT.portgroup, 'list')
def _test_generate_network_metadata(self, mock_portgroups, mock_ports,
address=None, vif_internal_info=True):
internal_info = ({'tenant_vif_port_id': utils.FAKE_VIF_UUID}
if vif_internal_info else {})
extra = ({'vif_port_id': utils.FAKE_VIF_UUID}
if not vif_internal_info else {})
portgroup = ironic_utils.get_test_portgroup(
node_uuid=self.node.uuid, address=address,
extra=extra, internal_info=internal_info,
properties={'bond_miimon': 100, 'xmit_hash_policy': 'layer3+4'}
)
port1 = ironic_utils.get_test_port(uuid=uuidutils.generate_uuid(),
node_uuid=self.node.uuid,
address='00:00:00:00:00:01',
portgroup_uuid=portgroup.uuid)
port2 = ironic_utils.get_test_port(uuid=uuidutils.generate_uuid(),
node_uuid=self.node.uuid,
address='00:00:00:00:00:02',
portgroup_uuid=portgroup.uuid)
mock_ports.return_value = [port1, port2]
mock_portgroups.return_value = [portgroup]
metadata = self.driver._get_network_metadata(self.node,
self.network_info)
pg_vif = metadata['links'][0]
self.assertEqual('bond', pg_vif['type'])
self.assertEqual('active-backup', pg_vif['bond_mode'])
self.assertEqual(address if address else utils.FAKE_VIF_MAC,
pg_vif['ethernet_mac_address'])
self.assertEqual('layer3+4',
pg_vif['bond_xmit_hash_policy'])
self.assertEqual(100, pg_vif['bond_miimon'])
self.assertEqual([port1.uuid, port2.uuid],
pg_vif['bond_links'])
self.assertEqual([{'id': port1.uuid, 'type': 'phy',
'ethernet_mac_address': port1.address},
{'id': port2.uuid, 'type': 'phy',
'ethernet_mac_address': port2.address}],
metadata['links'][1:])
# assert there are no duplicate links
link_ids = [link['id'] for link in metadata['links']]
self.assertEqual(len(set(link_ids)), len(link_ids),
'There are duplicate link IDs: %s' % link_ids)
def test_generate_network_metadata_with_pg_address(self, mock_cd_builder,
mock_instance_meta):
self._test_generate_network_metadata(address='00:00:00:00:00:00')
def test_generate_network_metadata_no_pg_address(self, mock_cd_builder,
mock_instance_meta):
self._test_generate_network_metadata()
def test_generate_network_metadata_vif_in_extra(self, mock_cd_builder,
mock_instance_meta):
self._test_generate_network_metadata(vif_internal_info=False)
@mock.patch.object(FAKE_CLIENT.node, 'list_ports')
@mock.patch.object(FAKE_CLIENT.portgroup, 'list')
def test_generate_network_metadata_ports_only(self, mock_portgroups,
mock_ports, mock_cd_builder,
mock_instance_meta):
address = self.network_info[0]['address']
port = ironic_utils.get_test_port(
node_uuid=self.node.uuid, address=address,
internal_info={'tenant_vif_port_id': utils.FAKE_VIF_UUID})
mock_ports.return_value = [port]
mock_portgroups.return_value = []
metadata = self.driver._get_network_metadata(self.node,
self.network_info)
+24
View File
@@ -59,6 +59,23 @@ def get_test_port(**kw):
'address': kw.get('address', 'FF:FF:FF:FF:FF:FF'),
'extra': kw.get('extra', {}),
'internal_info': kw.get('internal_info', {}),
'portgroup_uuid': kw.get('portgroup_uuid'),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at')})()
def get_test_portgroup(**kw):
return type('portgroup', (object,),
{'uuid': kw.get('uuid', 'deaffeed-1234-5678-9012-fedcbafedcba'),
'node_uuid': kw.get('node_uuid', get_test_node().uuid),
'address': kw.get('address', 'EE:EE:EE:EE:EE:EE'),
'extra': kw.get('extra', {}),
'internal_info': kw.get('internal_info', {}),
'properties': kw.get('properties', {}),
'mode': kw.get('mode', 'active-backup'),
'name': kw.get('name'),
'standalone_ports_supported': kw.get(
'standalone_ports_supported', True),
'created_at': kw.get('created_at'),
'updated_at': kw.get('updated_at')})()
@@ -110,6 +127,12 @@ class FakePortClient(object):
pass
class FakePortgroupClient(object):
def list(self, node=None, detail=False):
pass
class FakeNodeClient(object):
def list(self, detail=False):
@@ -147,3 +170,4 @@ class FakeClient(object):
node = FakeNodeClient()
port = FakePortClient()
portgroup = FakePortgroupClient()
+43 -9
View File
@@ -694,25 +694,59 @@ class IronicDriver(virt_driver.ComputeDriver):
"""
base_metadata = netutils.get_network_metadata(network_info)
# TODO(vdrok): change to doing a single "detailed vif list" call,
# when added to ironic API, response to that will contain all
# necessary information. Then we will be able to avoid looking at
# internal_info/extra fields.
ports = self.ironicclient.call("node.list_ports",
node.uuid, detail=True)
portgroups = self.ironicclient.call("portgroup.list", node=node.uuid,
detail=True)
vif_id_to_objects = {'ports': {}, 'portgroups': {}}
for collection, name in ((ports, 'ports'), (portgroups, 'portgroups')):
for p in collection:
vif_id = (p.internal_info.get('tenant_vif_port_id') or
p.extra.get('vif_port_id'))
if vif_id:
vif_id_to_objects[name][vif_id] = p
# TODO(vsaienko) add support of portgroups
vif_id_to_objects = {'ports': {}}
for p in ports:
vif_id = (p.internal_info.get('tenant_vif_port_id') or
p.extra.get('vif_port_id'))
if vif_id:
vif_id_to_objects['ports'][vif_id] = p
additional_links = []
for link in base_metadata['links']:
vif_id = link['vif_id']
if vif_id in vif_id_to_objects['ports']:
if vif_id in vif_id_to_objects['portgroups']:
pg = vif_id_to_objects['portgroups'][vif_id]
pg_ports = [p for p in ports if p.portgroup_uuid == pg.uuid]
link.update({'type': 'bond', 'bond_mode': pg.mode,
'bond_links': []})
# If address is set on the portgroup, an (ironic) vif-attach
# call has already updated neutron with the port address;
# reflect it here. Otherwise, an address generated by neutron
# will be used instead (code is elsewhere to handle this case).
if pg.address:
link.update({'ethernet_mac_address': pg.address})
for prop in pg.properties:
# These properties are the bonding driver options described
# at https://www.kernel.org/doc/Documentation/networking/bonding.txt # noqa
# cloud-init checks the same way, parameter name has to
# start with bond
key = prop if prop.startswith('bond') else 'bond_%s' % prop
link[key] = pg.properties[prop]
for port in pg_ports:
# This won't cause any duplicates to be added. A port
# cannot be in more than one port group for the same
# node.
additional_links.append({
'id': port.uuid,
'type': 'phy', 'ethernet_mac_address': port.address,
})
link['bond_links'].append(port.uuid)
elif vif_id in vif_id_to_objects['ports']:
p = vif_id_to_objects['ports'][vif_id]
# Ironic updates neutron port's address during attachment
link.update({'ethernet_mac_address': p.address,
'type': 'phy'})
base_metadata['links'].extend(additional_links)
return base_metadata
def _generate_configdrive(self, context, instance, node, network_info,
@@ -0,0 +1,7 @@
---
features:
- |
Updates the network metadata that is passed to configdrive by the Ironic
virt driver. The metadata now includes network information about port
groups and their associated ports. It will be used to configure port
groups on the baremetal instance side.