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
"""
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',
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,
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
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.
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.
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:
"""
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,
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]
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
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__)
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.
}
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
@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,
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
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.
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)