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
e239c03ee1
commit
3b32c7968f
@ -23,11 +23,11 @@ import inspect
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from novaclient.v1_1 import client as nova_client
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
|
from neutron.extensions import portbindings
|
||||||
from neutron.extensions import providernet as provider
|
from neutron.extensions import providernet as provider
|
||||||
from neutron import neutron_plugin_base_v2
|
from neutron import neutron_plugin_base_v2
|
||||||
from neutron.openstack.common import importutils
|
from neutron.openstack.common import importutils
|
||||||
@ -51,7 +51,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
"""
|
"""
|
||||||
MANAGE_STATE = True
|
MANAGE_STATE = True
|
||||||
__native_bulk_support = True
|
__native_bulk_support = True
|
||||||
supported_extension_aliases = ["provider"]
|
supported_extension_aliases = ["provider", "binding"]
|
||||||
_plugins = {}
|
_plugins = {}
|
||||||
_methods_to_delegate = ['create_network_bulk',
|
_methods_to_delegate = ['create_network_bulk',
|
||||||
'get_network', 'get_networks',
|
'get_network', 'get_networks',
|
||||||
@ -176,21 +176,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
raise cexc.NetworkSegmentIDNotFound(net_id=network_id)
|
raise cexc.NetworkSegmentIDNotFound(net_id=network_id)
|
||||||
return binding_seg_id.segmentation_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):
|
def _get_provider_vlan_id(self, network):
|
||||||
if (all(attributes.is_attr_set(network.get(attr))
|
if (all(attributes.is_attr_set(network.get(attr))
|
||||||
for attr in (provider.NETWORK_TYPE,
|
for attr in (provider.NETWORK_TYPE,
|
||||||
@ -275,7 +260,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _invoke_nexus_for_net_create(self, context, tenant_id, net_id,
|
def _invoke_nexus_for_net_create(self, context, tenant_id, net_id,
|
||||||
instance_id):
|
instance_id, host_id):
|
||||||
if not self.config_nexus:
|
if not self.config_nexus:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -287,16 +272,30 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
attachment = {
|
attachment = {
|
||||||
const.TENANT_ID: tenant_id,
|
const.TENANT_ID: tenant_id,
|
||||||
const.INSTANCE_ID: instance_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(
|
self._invoke_plugin_per_device(
|
||||||
const.NEXUS_PLUGIN,
|
const.NEXUS_PLUGIN,
|
||||||
'create_network',
|
'create_network',
|
||||||
[network, attachment])
|
[network, attachment])
|
||||||
|
|
||||||
@staticmethod
|
def _check_valid_port_device_owner(self, port):
|
||||||
def _should_call_create_net(device_owner, instance_id):
|
"""Check the port for valid device_owner.
|
||||||
return (instance_id and device_owner != 'network:dhcp')
|
|
||||||
|
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):
|
def create_port(self, context, port):
|
||||||
"""Create port.
|
"""Create port.
|
||||||
@ -309,16 +308,18 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||||
self._func_name(),
|
self._func_name(),
|
||||||
args)
|
args)
|
||||||
try:
|
|
||||||
instance_id = port['port']['device_id']
|
instance_id = port['port']['device_id']
|
||||||
device_owner = port['port']['device_owner']
|
|
||||||
|
|
||||||
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']
|
net_id = port['port']['network_id']
|
||||||
tenant_id = port['port']['tenant_id']
|
tenant_id = port['port']['tenant_id']
|
||||||
self._invoke_nexus_for_net_create(
|
self._invoke_nexus_for_net_create(
|
||||||
context, tenant_id, net_id, instance_id)
|
context, tenant_id, net_id, instance_id, host_id)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Create network on the Nexus plugin has failed, so we need
|
# Create network on the Nexus plugin has failed, so we need
|
||||||
# to rollback the port creation on the VSwitch plugin.
|
# 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,
|
ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
|
||||||
self._func_name(),
|
self._func_name(),
|
||||||
args)
|
args)
|
||||||
try:
|
|
||||||
net_id = old_port['network_id']
|
net_id = old_port['network_id']
|
||||||
instance_id = ''
|
instance_id = ''
|
||||||
if 'device_id' in port['port']:
|
if 'device_id' in port['port']:
|
||||||
instance_id = port['port']['device_id']
|
instance_id = port['port']['device_id']
|
||||||
|
|
||||||
# Check if there's a new device_id
|
# Check if there's a new device_id
|
||||||
if instance_id and not old_device:
|
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']
|
tenant_id = old_port['tenant_id']
|
||||||
self._invoke_nexus_for_net_create(
|
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]
|
return ovs_output[0]
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -392,8 +395,11 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
"""
|
"""
|
||||||
LOG.debug(_("delete_port() called"))
|
LOG.debug(_("delete_port() called"))
|
||||||
port = self.get_port(context, id)
|
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'])
|
vlan_id = self._get_segmentation_id(port['network_id'])
|
||||||
n_args = [port['device_id'], vlan_id]
|
n_args = [port['device_id'], vlan_id]
|
||||||
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
|
||||||
@ -411,8 +417,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
|
|||||||
tenant_id = port['tenant_id']
|
tenant_id = port['tenant_id']
|
||||||
net_id = port['network_id']
|
net_id = port['network_id']
|
||||||
instance_id = port['device_id']
|
instance_id = port['device_id']
|
||||||
self._invoke_nexus_for_net_create(context, tenant_id,
|
host_id = port[portbindings.HOST_ID]
|
||||||
net_id, instance_id)
|
self._invoke_nexus_for_net_create(context, tenant_id, net_id,
|
||||||
|
instance_id, host_id)
|
||||||
finally:
|
finally:
|
||||||
# Raise the original exception.
|
# Raise the original exception.
|
||||||
raise exc_info[0], exc_info[1], exc_info[2]
|
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 import context
|
||||||
from neutron.db import db_base_plugin_v2 as base_plugin
|
from neutron.db import db_base_plugin_v2 as base_plugin
|
||||||
from neutron.db import l3_db
|
from neutron.db import l3_db
|
||||||
|
from neutron.extensions import portbindings
|
||||||
from neutron.extensions import providernet as provider
|
from neutron.extensions import providernet as provider
|
||||||
from neutron.manager import NeutronManager
|
from neutron.manager import NeutronManager
|
||||||
from neutron.plugins.cisco.common import cisco_constants as const
|
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.cisco.models import virt_phy_sw_v2
|
||||||
from neutron.plugins.openvswitch.common import config as ovs_config
|
from neutron.plugins.openvswitch.common import config as ovs_config
|
||||||
from neutron.plugins.openvswitch import ovs_db_v2
|
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
|
from neutron.tests.unit import test_db_plugin
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -83,7 +85,8 @@ class TestCiscoV2HTTPResponse(CiscoNetworkPluginV2TestCase,
|
|||||||
|
|
||||||
|
|
||||||
class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||||
test_db_plugin.TestPortsV2):
|
test_db_plugin.TestPortsV2,
|
||||||
|
test_bindings.PortBindingsHostTestCaseMixin):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Configure for end-to-end neutron testing using a mock ncclient.
|
"""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()
|
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()
|
super(TestCiscoPortsV2, self).setUp()
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
@ -169,7 +162,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
|||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _create_port_res(self, name='myname', cidr='1.0.0.0/24',
|
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 and yield the result.
|
||||||
|
|
||||||
Create a network, subnet, and port, yield the result,
|
Create a network, subnet, and port, yield the result,
|
||||||
@ -181,12 +174,16 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
|||||||
end of testing
|
end of testing
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
ctx = context.get_admin_context()
|
||||||
with self.network(name=name) as network:
|
with self.network(name=name) as network:
|
||||||
with self.subnet(network=network, cidr=cidr) as subnet:
|
with self.subnet(network=network, cidr=cidr) as subnet:
|
||||||
net_id = subnet['subnet']['network_id']
|
net_id = subnet['subnet']['network_id']
|
||||||
res = self._create_port(self.fmt, net_id,
|
args = (portbindings.HOST_ID, 'device_id', 'device_owner')
|
||||||
device_id='testdev',
|
port_dict = {portbindings.HOST_ID: host_id,
|
||||||
device_owner='testowner')
|
'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)
|
port = self.deserialize(self.fmt, res)
|
||||||
try:
|
try:
|
||||||
yield res
|
yield res
|
||||||
@ -405,10 +402,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
|||||||
a fictitious host name during port creation.
|
a fictitious host name during port creation.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
|
with self._create_port_res(do_delete=False, host_id='fakehost') as res:
|
||||||
'_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,
|
self._assertExpectedHTTP(res.status_int,
|
||||||
c_exc.NexusComputeHostNotConfigured)
|
c_exc.NexusComputeHostNotConfigured)
|
||||||
|
|
||||||
@ -450,7 +444,8 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
|||||||
device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1"
|
device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1"
|
||||||
if orig_port['port']['device_id'] == device_id:
|
if orig_port['port']['device_id'] == device_id:
|
||||||
device_id = "600df00d-e4a8-4a3a-8906-feed600df00d"
|
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']
|
port_id = orig_port['port']['id']
|
||||||
req = self.new_update_request('ports', data, port_id)
|
req = self.new_update_request('ports', data, port_id)
|
||||||
res = req.get_response(self.api)
|
res = req.get_response(self.api)
|
||||||
|
Loading…
Reference in New Issue
Block a user