Merge "Allow per-port modification of vnic_type and profile"
This commit is contained in:
@@ -399,7 +399,7 @@ class NetworkAPI(base.Base):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def bind_ports_to_host(self, context, instance, host,
|
def bind_ports_to_host(self, context, instance, host,
|
||||||
vnic_type=None, profile=None):
|
vnic_types=None, port_profiles=None):
|
||||||
"""Attempts to bind the ports from the instance on the given host
|
"""Attempts to bind the ports from the instance on the given host
|
||||||
|
|
||||||
If the ports are already actively bound to another host, like the
|
If the ports are already actively bound to another host, like the
|
||||||
@@ -420,17 +420,17 @@ class NetworkAPI(base.Base):
|
|||||||
:param host: the host on which to bind the ports which
|
:param host: the host on which to bind the ports which
|
||||||
are attached to the instance
|
are attached to the instance
|
||||||
:type host: str
|
:type host: str
|
||||||
:param vnic_type: optional vnic type string for the host
|
:param vnic_types: optional dict for the host port binding
|
||||||
port binding
|
:type vnic_types: dict of <port_id> : <vnic_type>
|
||||||
:type vnic_type: str
|
:param port_profiles: optional dict per port ID for the host port
|
||||||
:param profile: optional vif profile dict for the host port
|
binding profile.
|
||||||
binding; note that the port binding profile is mutable
|
note that the port binding profile is mutable
|
||||||
via the networking "Port Binding" API so callers that
|
via the networking "Port Binding" API so callers that
|
||||||
pass in a profile should ensure they have the latest
|
pass in a profile should ensure they have the latest
|
||||||
version from neutron with their changes merged,
|
version from neutron with their changes merged,
|
||||||
which can be determined using the "revision_number"
|
which can be determined using the "revision_number"
|
||||||
attribute of the port.
|
attribute of the port.
|
||||||
:type profile: dict
|
:type port_profiles: dict of <port_id> : <port_profile>
|
||||||
:raises: PortBindingFailed if any of the ports failed to be bound to
|
:raises: PortBindingFailed if any of the ports failed to be bound to
|
||||||
the destination host
|
the destination host
|
||||||
:returns: dict, keyed by port ID, of a new host port
|
:returns: dict, keyed by port ID, of a new host port
|
||||||
|
|||||||
@@ -1299,7 +1299,7 @@ class API(base_api.NetworkAPI):
|
|||||||
return constants.PORT_BINDING_EXTENDED in self.extensions
|
return constants.PORT_BINDING_EXTENDED in self.extensions
|
||||||
|
|
||||||
def bind_ports_to_host(self, context, instance, host,
|
def bind_ports_to_host(self, context, instance, host,
|
||||||
vnic_type=None, profile=None):
|
vnic_types=None, port_profiles=None):
|
||||||
"""Attempts to bind the ports from the instance on the given host
|
"""Attempts to bind the ports from the instance on the given host
|
||||||
|
|
||||||
If the ports are already actively bound to another host, like the
|
If the ports are already actively bound to another host, like the
|
||||||
@@ -1320,17 +1320,17 @@ class API(base_api.NetworkAPI):
|
|||||||
:param host: the host on which to bind the ports which
|
:param host: the host on which to bind the ports which
|
||||||
are attached to the instance
|
are attached to the instance
|
||||||
:type host: str
|
:type host: str
|
||||||
:param vnic_type: optional vnic type string for the host
|
:param vnic_types: optional dict for the host port binding
|
||||||
port binding
|
:type vnic_types: dict of <port_id> : <vnic_type>
|
||||||
:type vnic_type: str
|
:param port_profiles: optional dict per port ID for the host port
|
||||||
:param profile: optional vif profile dict for the host port
|
binding profile.
|
||||||
binding; note that the port binding profile is mutable
|
note that the port binding profile is mutable
|
||||||
via the networking "Port Binding" API so callers that
|
via the networking "Port Binding" API so callers that
|
||||||
pass in a profile should ensure they have the latest
|
pass in a profile should ensure they have the latest
|
||||||
version from neutron with their changes merged,
|
version from neutron with their changes merged,
|
||||||
which can be determined using the "revision_number"
|
which can be determined using the "revision_number"
|
||||||
attribute of the port.
|
attribute of the port.
|
||||||
:type profile: dict
|
:type port_profiles: dict of <port_id> : <port_profile>
|
||||||
:raises: PortBindingFailed if any of the ports failed to be bound to
|
:raises: PortBindingFailed if any of the ports failed to be bound to
|
||||||
the destination host
|
the destination host
|
||||||
:returns: dict, keyed by port ID, of a new host port
|
:returns: dict, keyed by port ID, of a new host port
|
||||||
@@ -1339,31 +1339,36 @@ class API(base_api.NetworkAPI):
|
|||||||
# Get the current ports off the instance. This assumes the cache is
|
# Get the current ports off the instance. This assumes the cache is
|
||||||
# current.
|
# current.
|
||||||
network_info = instance.get_network_info()
|
network_info = instance.get_network_info()
|
||||||
port_ids = [vif['id'] for vif in network_info]
|
|
||||||
|
|
||||||
if not port_ids:
|
if not network_info:
|
||||||
# The instance doesn't have any ports so there is nothing to do.
|
# The instance doesn't have any ports so there is nothing to do.
|
||||||
LOG.debug('Instance does not have any ports.', instance=instance)
|
LOG.debug('Instance does not have any ports.', instance=instance)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
client = _get_ksa_client(context, admin=True)
|
client = _get_ksa_client(context, admin=True)
|
||||||
|
|
||||||
# Now bind each port to the destination host and keep track of each
|
|
||||||
# port that is bound to the resulting binding so we can rollback in
|
|
||||||
# the event of a failure, or return the results if everything is OK.
|
|
||||||
binding = dict(host=host)
|
|
||||||
if vnic_type:
|
|
||||||
binding['vnic_type'] = vnic_type
|
|
||||||
if profile:
|
|
||||||
binding['profile'] = profile
|
|
||||||
data = dict(binding=binding)
|
|
||||||
|
|
||||||
# TODO(gibi): To support ports with resource request during server
|
# TODO(gibi): To support ports with resource request during server
|
||||||
# live migrate operation we need to take care of 'allocation' key in
|
# live migrate operation we need to take care of 'allocation' key in
|
||||||
# the binding profile per binding.
|
# the binding profile per binding.
|
||||||
|
|
||||||
bindings_by_port_id = {}
|
bindings_by_port_id = {}
|
||||||
for port_id in port_ids:
|
for vif in network_info:
|
||||||
|
# Now bind each port to the destination host and keep track of each
|
||||||
|
# port that is bound to the resulting binding so we can rollback in
|
||||||
|
# the event of a failure, or return the results if everything is OK
|
||||||
|
port_id = vif['id']
|
||||||
|
binding = dict(host=host)
|
||||||
|
if vnic_types is None or port_id not in vnic_types:
|
||||||
|
binding['vnic_type'] = vif['vnic_type']
|
||||||
|
else:
|
||||||
|
binding['vnic_type'] = vnic_types[port_id]
|
||||||
|
|
||||||
|
if port_profiles is None or port_id not in port_profiles:
|
||||||
|
binding['profile'] = vif['profile']
|
||||||
|
else:
|
||||||
|
binding['profile'] = port_profiles[port_id]
|
||||||
|
|
||||||
|
data = dict(binding=binding)
|
||||||
resp = self._create_port_binding(client, port_id, data)
|
resp = self._create_port_binding(client, port_id, data)
|
||||||
if resp:
|
if resp:
|
||||||
bindings_by_port_id[port_id] = resp.json()['binding']
|
bindings_by_port_id[port_id] = resp.json()['binding']
|
||||||
|
|||||||
@@ -6352,6 +6352,10 @@ class TestPortBindingWithMock(test.NoDBTestCase):
|
|||||||
@mock.patch('nova.network.neutronv2.api._get_ksa_client')
|
@mock.patch('nova.network.neutronv2.api._get_ksa_client')
|
||||||
def test_bind_ports_to_host(self, mock_client):
|
def test_bind_ports_to_host(self, mock_client):
|
||||||
"""Tests a single port happy path where everything is successful."""
|
"""Tests a single port happy path where everything is successful."""
|
||||||
|
def post_side_effect(*args, **kwargs):
|
||||||
|
self.assertDictEqual(binding, kwargs['json'])
|
||||||
|
return mock.DEFAULT
|
||||||
|
|
||||||
nwinfo = model.NetworkInfo([model.VIF(uuids.port)])
|
nwinfo = model.NetworkInfo([model.VIF(uuids.port)])
|
||||||
inst = objects.Instance(
|
inst = objects.Instance(
|
||||||
info_cache=objects.InstanceInfoCache(network_info=nwinfo))
|
info_cache=objects.InstanceInfoCache(network_info=nwinfo))
|
||||||
@@ -6359,11 +6363,58 @@ class TestPortBindingWithMock(test.NoDBTestCase):
|
|||||||
binding = {'binding': {'host': 'fake-host',
|
binding = {'binding': {'host': 'fake-host',
|
||||||
'vnic_type': 'normal',
|
'vnic_type': 'normal',
|
||||||
'profile': {'foo': 'bar'}}}
|
'profile': {'foo': 'bar'}}}
|
||||||
|
|
||||||
|
resp = fake_req.FakeResponse(200, content=jsonutils.dumps(binding))
|
||||||
|
mock_client.return_value.post.return_value = resp
|
||||||
|
mock_client.return_value.post.side_effect = post_side_effect
|
||||||
|
result = self.api.bind_ports_to_host(
|
||||||
|
ctxt, inst, 'fake-host', {uuids.port: 'normal'},
|
||||||
|
{uuids.port: {'foo': 'bar'}})
|
||||||
|
self.assertEqual(1, mock_client.return_value.post.call_count)
|
||||||
|
self.assertDictEqual({uuids.port: binding['binding']}, result)
|
||||||
|
|
||||||
|
@mock.patch('nova.network.neutronv2.api._get_ksa_client')
|
||||||
|
def test_bind_ports_to_host_with_vif_profile_and_vnic(self, mock_client):
|
||||||
|
"""Tests bind_ports_to_host with default/non-default parameters."""
|
||||||
|
def post_side_effect(*args, **kwargs):
|
||||||
|
self.assertDictEqual(binding, kwargs['json'])
|
||||||
|
return mock.DEFAULT
|
||||||
|
|
||||||
|
ctxt = context.get_context()
|
||||||
|
vif_profile = {'foo': 'default'}
|
||||||
|
nwinfo = model.NetworkInfo([model.VIF(id=uuids.port,
|
||||||
|
vnic_type="direct",
|
||||||
|
profile=vif_profile)])
|
||||||
|
inst = objects.Instance(
|
||||||
|
info_cache=objects.InstanceInfoCache(network_info=nwinfo))
|
||||||
|
binding = {'binding': {'host': 'fake-host',
|
||||||
|
'vnic_type': 'direct',
|
||||||
|
'profile': vif_profile}}
|
||||||
|
resp = fake_req.FakeResponse(200, content=jsonutils.dumps(binding))
|
||||||
|
mock_client.return_value.post.return_value = resp
|
||||||
|
mock_client.return_value.post.side_effect = post_side_effect
|
||||||
|
result = self.api.bind_ports_to_host(ctxt, inst, 'fake-host')
|
||||||
|
self.assertEqual(1, mock_client.return_value.post.call_count)
|
||||||
|
self.assertDictEqual({uuids.port: binding['binding']}, result)
|
||||||
|
|
||||||
|
# assert that that if vnic_type and profile are set in VIF object
|
||||||
|
# the provided vnic_type and profile take precedence.
|
||||||
|
|
||||||
|
nwinfo = model.NetworkInfo([model.VIF(id=uuids.port,
|
||||||
|
vnic_type='direct',
|
||||||
|
profile=vif_profile)])
|
||||||
|
inst = objects.Instance(
|
||||||
|
info_cache=objects.InstanceInfoCache(network_info=nwinfo))
|
||||||
|
vif_profile_per_port = {uuids.port: {'foo': 'overridden'}}
|
||||||
|
vnic_type_per_port = {uuids.port: "direct-overridden"}
|
||||||
|
binding = {'binding': {'host': 'fake-host',
|
||||||
|
'vnic_type': 'direct-overridden',
|
||||||
|
'profile': {'foo': 'overridden'}}}
|
||||||
resp = fake_req.FakeResponse(200, content=jsonutils.dumps(binding))
|
resp = fake_req.FakeResponse(200, content=jsonutils.dumps(binding))
|
||||||
mock_client.return_value.post.return_value = resp
|
mock_client.return_value.post.return_value = resp
|
||||||
result = self.api.bind_ports_to_host(
|
result = self.api.bind_ports_to_host(
|
||||||
ctxt, inst, 'fake-host', 'normal', {'foo': 'bar'})
|
ctxt, inst, 'fake-host', vnic_type_per_port, vif_profile_per_port)
|
||||||
self.assertEqual(1, mock_client.return_value.post.call_count)
|
self.assertEqual(2, mock_client.return_value.post.call_count)
|
||||||
self.assertDictEqual({uuids.port: binding['binding']}, result)
|
self.assertDictEqual({uuids.port: binding['binding']}, result)
|
||||||
|
|
||||||
@mock.patch('nova.network.neutronv2.api._get_ksa_client')
|
@mock.patch('nova.network.neutronv2.api._get_ksa_client')
|
||||||
|
|||||||
Reference in New Issue
Block a user