Cisco plugin portbinding extension support
This commit adds portbinding extension support to the Cisco plugin. Change-Id: I87554607860b040b693edeecc2706ca8edbe49b6 Fixes: Bug #1218033
This commit is contained in:
parent
984d6f2b50
commit
3cc8e78956
@ -23,11 +23,11 @@ import inspect
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from novaclient.v1_1 import client as nova_client
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.api.v2 import attributes
|
||||
from neutron.db import api as db_api
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron import neutron_plugin_base_v2
|
||||
from neutron.openstack.common import importutils
|
||||
@ -51,7 +51,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""
|
||||
MANAGE_STATE = True
|
||||
__native_bulk_support = True
|
||||
supported_extension_aliases = ["provider"]
|
||||
supported_extension_aliases = ["provider", "binding"]
|
||||
_plugins = {}
|
||||
_methods_to_delegate = ['create_network_bulk',
|
||||
'get_network', 'get_networks',
|
||||
@ -176,21 +176,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
raise cexc.NetworkSegmentIDNotFound(net_id=network_id)
|
||||
return binding_seg_id.segmentation_id
|
||||
|
||||
def _get_instance_host(self, tenant_id, instance_id):
|
||||
keystone_conf = cfg.CONF.keystone_authtoken
|
||||
keystone_auth_url = '%s://%s:%s/v2.0/' % (keystone_conf.auth_protocol,
|
||||
keystone_conf.auth_host,
|
||||
keystone_conf.auth_port)
|
||||
nc = nova_client.Client(keystone_conf.admin_user,
|
||||
keystone_conf.admin_password,
|
||||
keystone_conf.admin_tenant_name,
|
||||
keystone_auth_url,
|
||||
no_cache=True)
|
||||
serv = nc.servers.get(instance_id)
|
||||
host = serv.__getattr__('OS-EXT-SRV-ATTR:host')
|
||||
|
||||
return host
|
||||
|
||||
def _get_provider_vlan_id(self, network):
|
||||
if (all(attributes.is_attr_set(network.get(attr))
|
||||
for attr in (provider.NETWORK_TYPE,
|
||||
@ -275,7 +260,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
pass
|
||||
|
||||
def _invoke_nexus_for_net_create(self, context, tenant_id, net_id,
|
||||
instance_id):
|
||||
instance_id, host_id):
|
||||
if not self.config_nexus:
|
||||
return False
|
||||
|
||||
@ -287,16 +272,30 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
attachment = {
|
||||
const.TENANT_ID: tenant_id,
|
||||
const.INSTANCE_ID: instance_id,
|
||||
const.HOST_NAME: self._get_instance_host(tenant_id, instance_id),
|
||||
const.HOST_NAME: host_id,
|
||||
}
|
||||
self._invoke_plugin_per_device(
|
||||
const.NEXUS_PLUGIN,
|
||||
'create_network',
|
||||
[network, attachment])
|
||||
|
||||
@staticmethod
|
||||
def _should_call_create_net(device_owner, instance_id):
|
||||
return (instance_id and device_owner != 'network:dhcp')
|
||||
def _check_valid_port_device_owner(self, port):
|
||||
"""Check the port for valid device_owner.
|
||||
|
||||
Don't call the nexus plugin for router and dhcp
|
||||
port owners.
|
||||
"""
|
||||
return port['device_owner'].startswith('compute')
|
||||
|
||||
def _get_port_host_id_from_bindings(self, port):
|
||||
"""Get host_id from portbindings."""
|
||||
host_id = None
|
||||
|
||||
if (portbindings.HOST_ID in port and
|
||||
attributes.is_attr_set(port[portbindings.HOST_ID])):
|
||||
host_id = port[portbindings.HOST_ID]
|
||||
|
||||
return host_id
|
||||
|
||||
def create_port(self, context, port):
|
||||
"""Create port.
|
||||
@ -309,16 +308,18 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
try:
|
||||
instance_id = port['port']['device_id']
|
||||
device_owner = port['port']['device_owner']
|
||||
instance_id = port['port']['device_id']
|
||||
|
||||
if self._should_call_create_net(device_owner, instance_id):
|
||||
# Only call nexus plugin if there's a valid instance_id, host_id
|
||||
# and device_owner
|
||||
try:
|
||||
host_id = self._get_port_host_id_from_bindings(port['port'])
|
||||
if (instance_id and host_id and
|
||||
self._check_valid_port_device_owner(port['port'])):
|
||||
net_id = port['port']['network_id']
|
||||
tenant_id = port['port']['tenant_id']
|
||||
self._invoke_nexus_for_net_create(
|
||||
context, tenant_id, net_id, instance_id)
|
||||
|
||||
context, tenant_id, net_id, instance_id, host_id)
|
||||
except Exception:
|
||||
# Create network on the Nexus plugin has failed, so we need
|
||||
# to rollback the port creation on the VSwitch plugin.
|
||||
@ -356,17 +357,19 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||
self._func_name(),
|
||||
args)
|
||||
try:
|
||||
net_id = old_port['network_id']
|
||||
instance_id = ''
|
||||
if 'device_id' in port['port']:
|
||||
instance_id = port['port']['device_id']
|
||||
net_id = old_port['network_id']
|
||||
instance_id = ''
|
||||
if 'device_id' in port['port']:
|
||||
instance_id = port['port']['device_id']
|
||||
|
||||
# Check if there's a new device_id
|
||||
if instance_id and not old_device:
|
||||
# Check if there's a new device_id
|
||||
try:
|
||||
host_id = self._get_port_host_id_from_bindings(port['port'])
|
||||
if (instance_id and not old_device and host_id and
|
||||
self._check_valid_port_device_owner(port['port'])):
|
||||
tenant_id = old_port['tenant_id']
|
||||
self._invoke_nexus_for_net_create(
|
||||
context, tenant_id, net_id, instance_id)
|
||||
context, tenant_id, net_id, instance_id, host_id)
|
||||
|
||||
return ovs_output[0]
|
||||
except Exception:
|
||||
@ -392,8 +395,11 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
"""
|
||||
LOG.debug(_("delete_port() called"))
|
||||
port = self.get_port(context, id)
|
||||
exclude_list = ('', 'compute:none', 'network:dhcp')
|
||||
if self.config_nexus and port['device_owner'] not in exclude_list:
|
||||
|
||||
host_id = self._get_port_host_id_from_bindings(port)
|
||||
|
||||
if (self.config_nexus and host_id and
|
||||
self._check_valid_port_device_owner(port)):
|
||||
vlan_id = self._get_segmentation_id(port['network_id'])
|
||||
n_args = [port['device_id'], vlan_id]
|
||||
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||
@ -411,8 +417,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
||||
tenant_id = port['tenant_id']
|
||||
net_id = port['network_id']
|
||||
instance_id = port['device_id']
|
||||
self._invoke_nexus_for_net_create(context, tenant_id,
|
||||
net_id, instance_id)
|
||||
host_id = port[portbindings.HOST_ID]
|
||||
self._invoke_nexus_for_net_create(context, tenant_id, net_id,
|
||||
instance_id, host_id)
|
||||
finally:
|
||||
# Raise the original exception.
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
|
@ -26,6 +26,7 @@ from neutron.common import exceptions as q_exc
|
||||
from neutron import context
|
||||
from neutron.db import db_base_plugin_v2 as base_plugin
|
||||
from neutron.db import l3_db
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.extensions import providernet as provider
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.plugins.cisco.common import cisco_constants as const
|
||||
@ -35,6 +36,7 @@ from neutron.plugins.cisco.db import nexus_db_v2
|
||||
from neutron.plugins.cisco.models import virt_phy_sw_v2
|
||||
from neutron.plugins.openvswitch.common import config as ovs_config
|
||||
from neutron.plugins.openvswitch import ovs_db_v2
|
||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||
from neutron.tests.unit import test_db_plugin
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -83,7 +85,8 @@ class TestCiscoV2HTTPResponse(CiscoNetworkPluginV2TestCase,
|
||||
|
||||
|
||||
class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
test_db_plugin.TestPortsV2):
|
||||
test_db_plugin.TestPortsV2,
|
||||
test_bindings.PortBindingsHostTestCaseMixin):
|
||||
|
||||
def setUp(self):
|
||||
"""Configure for end-to-end neutron testing using a mock ncclient.
|
||||
@ -135,16 +138,6 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
}
|
||||
mock.patch.dict(cisco_config.device_dictionary, nexus_config).start()
|
||||
|
||||
patches = {
|
||||
'_should_call_create_net': True,
|
||||
'_get_instance_host': 'testhost'
|
||||
}
|
||||
for func in patches:
|
||||
mock_sw = mock.patch.object(
|
||||
virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
|
||||
func).start()
|
||||
mock_sw.return_value = patches[func]
|
||||
|
||||
super(TestCiscoPortsV2, self).setUp()
|
||||
|
||||
@contextlib.contextmanager
|
||||
@ -169,7 +162,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _create_port_res(self, name='myname', cidr='1.0.0.0/24',
|
||||
do_delete=True):
|
||||
do_delete=True, host_id='testhost'):
|
||||
"""Create a network, subnet, and port and yield the result.
|
||||
|
||||
Create a network, subnet, and port, yield the result,
|
||||
@ -181,12 +174,16 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
end of testing
|
||||
|
||||
"""
|
||||
ctx = context.get_admin_context()
|
||||
with self.network(name=name) as network:
|
||||
with self.subnet(network=network, cidr=cidr) as subnet:
|
||||
net_id = subnet['subnet']['network_id']
|
||||
res = self._create_port(self.fmt, net_id,
|
||||
device_id='testdev',
|
||||
device_owner='testowner')
|
||||
args = (portbindings.HOST_ID, 'device_id', 'device_owner')
|
||||
port_dict = {portbindings.HOST_ID: host_id,
|
||||
'device_id': 'testdev',
|
||||
'device_owner': 'compute:None'}
|
||||
res = self._create_port(self.fmt, net_id, arg_list=args,
|
||||
context=ctx, **port_dict)
|
||||
port = self.deserialize(self.fmt, res)
|
||||
try:
|
||||
yield res
|
||||
@ -405,12 +402,9 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
a fictitious host name during port creation.
|
||||
|
||||
"""
|
||||
with mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
|
||||
'_get_instance_host') as mock_get_instance:
|
||||
mock_get_instance.return_value = 'fictitious_host'
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NexusComputeHostNotConfigured)
|
||||
with self._create_port_res(do_delete=False, host_id='fakehost') as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NexusComputeHostNotConfigured)
|
||||
|
||||
def test_nexus_bind_fail_rollback(self):
|
||||
"""Test for proper rollback following add Nexus DB binding failure.
|
||||
@ -450,7 +444,8 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1"
|
||||
if orig_port['port']['device_id'] == device_id:
|
||||
device_id = "600df00d-e4a8-4a3a-8906-feed600df00d"
|
||||
data = {'port': {'device_id': device_id}}
|
||||
data = {'port': {'device_id': device_id,
|
||||
portbindings.HOST_ID: 'testhost'}}
|
||||
port_id = orig_port['port']['id']
|
||||
req = self.new_update_request('ports', data, port_id)
|
||||
res = req.get_response(self.api)
|
||||
|
Loading…
Reference in New Issue
Block a user