Skip to content

Commit

Permalink
Merge "Fix Cisco nexus plugin failure for first VLAN on phy interface…
Browse files Browse the repository at this point in the history
…" into stable/grizzly
  • Loading branch information
Jenkins authored and openstack-gerrit committed Jul 1, 2013
2 parents 06f679d + 678690e commit b9daf57
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 56 deletions.
68 changes: 30 additions & 38 deletions quantum/plugins/cisco/db/nexus_db_v2.py
Expand Up @@ -33,38 +33,26 @@ def get_all_nexusport_bindings():
"""Lists all the nexusport bindings"""
LOG.debug(_("get_all_nexusport_bindings() called"))
session = db.get_session()
try:
bindings = session.query(nexus_models_v2.NexusPortBinding).all()
return bindings
except exc.NoResultFound:
return []
return session.query(nexus_models_v2.NexusPortBinding).all()


def get_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
"""Lists a nexusport binding"""
LOG.debug(_("get_nexusport_binding() called"))
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
return (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())


def get_nexusvlan_binding(vlan_id, switch_ip):
"""Lists a vlan and switch binding"""
LOG.debug(_("get_nexusvlan_binding() called"))
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
return (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
all())


def add_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
Expand All @@ -82,18 +70,15 @@ def remove_nexusport_binding(port_id, vlan_id, switch_ip, instance_id):
"""Removes a nexusport binding"""
LOG.debug(_("remove_nexusport_binding() called"))
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(vlan_id=vlan_id).filter_by(switch_ip=switch_ip).
filter_by(port_id=port_id).
filter_by(instance_id=instance_id).all())

for bind in binding:
session.delete(bind)
session.flush()
return binding
except exc.NoResultFound:
pass
for bind in binding:
session.delete(bind)
session.flush()
return binding


def update_nexusport_binding(port_id, new_vlan_id):
Expand Down Expand Up @@ -129,10 +114,17 @@ def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
"""Lists nexusvm bindings"""
LOG.debug(_("get_port_vlan_switch_binding() called"))
session = db.get_session()
try:
binding = (session.query(nexus_models_v2.NexusPortBinding).
filter_by(port_id=port_id).filter_by(switch_ip=switch_ip).
filter_by(vlan_id=vlan_id).all())
return binding
except exc.NoResultFound:
raise c_exc.NexusPortBindingNotFound(vlan_id=vlan_id)
return (session.query(nexus_models_v2.NexusPortBinding).
filter_by(port_id=port_id).filter_by(switch_ip=switch_ip).
filter_by(vlan_id=vlan_id).all())


def get_port_switch_bindings(port_id, switch_ip):
"""List all vm/vlan bindings on a Nexus switch port."""
LOG.debug(_("get_port_switch_bindings() called, "
"port:'%(port_id)s', switch:'%(switch_ip)s'"),
{'port_id': port_id, 'switch_ip': switch_ip})
session = db.get_session()
return (session.query(nexus_models_v2.NexusPortBinding).
filter_by(port_id=port_id).
filter_by(switch_ip=switch_ip).all())
6 changes: 1 addition & 5 deletions quantum/plugins/cisco/models/virt_phy_sw_v2.py
Expand Up @@ -332,11 +332,7 @@ def create_port(self, context, port):
instance_id = port['port']['device_id']
device_owner = port['port']['device_owner']

if hasattr(conf, 'TEST'):
host = conf.TEST['host']
elif device_owner == 'network:dhcp':
return ovs_output[0]
elif instance_id:
if instance_id and device_owner != 'network:dhcp':
net_id = port['port']['network_id']
tenant_id = port['port']['tenant_id']
self._invoke_nexus_for_net_create(
Expand Down
22 changes: 14 additions & 8 deletions quantum/plugins/cisco/nexus/cisco_nexus_network_driver_v2.py
Expand Up @@ -27,6 +27,7 @@
from ncclient import manager

from quantum.plugins.cisco.db import network_db_v2 as cdb
from quantum.plugins.cisco.db import nexus_db_v2
from quantum.plugins.cisco.nexus import cisco_nexus_snippets as snipp


Expand Down Expand Up @@ -90,13 +91,18 @@ def disable_switch_port(self, mgr, interface):
LOG.debug(_("NexusDriver: %s"), confstr)
mgr.edit_config(target='running', config=confstr)

def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
"""
Enables trunk mode vlan access an interface on Nexus Switch given
VLANID
def enable_vlan_on_trunk_int(self, mgr, nexus_switch, interface, vlanid):
"""Enable vlan in trunk interface.
Enables trunk mode vlan access on an interface on Nexus Switch given
VLANID. If one or more VLANs are already configured on this
interface, include the 'add' keyword in the interface configuration.
"""
confstr = snipp.CMD_VLAN_INT_SNIPPET % (interface, vlanid)
confstr = self.create_xml_snippet(confstr)
if nexus_db_v2.get_port_switch_bindings(interface, nexus_switch):
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
else:
snippet = snipp.CMD_INT_VLAN_SNIPPET
confstr = self.create_xml_snippet(snippet % (interface, vlanid))
LOG.debug(_("NexusDriver: %s"), confstr)
mgr.edit_config(target='running', config=confstr)

Expand Down Expand Up @@ -124,7 +130,7 @@ def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user,
vlan_ids = self.build_vlans_cmd()
LOG.debug(_("NexusDriver VLAN IDs: %s"), vlan_ids)
for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)

def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port):
Expand Down Expand Up @@ -160,7 +166,7 @@ def add_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
if not vlan_ids:
vlan_ids = self.build_vlans_cmd()
for ports in nexus_ports:
self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)

def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
nexus_ports, nexus_ssh_port):
Expand Down
24 changes: 19 additions & 5 deletions quantum/plugins/cisco/nexus/cisco_nexus_snippets.py
Expand Up @@ -66,18 +66,24 @@
</no>
"""

CMD_VLAN_INT_SNIPPET = """
CMD_INT_VLAN_HEADER = """
<interface>
<ethernet>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport>
<trunk>
<allowed>
<vlan>
<add>
<add_vlans>%s</add_vlans>
</add>
<vlan>"""

CMD_VLAN_ID = """
<vlan_id>%s</vlan_id>"""

CMD_VLAN_ADD_ID = """
<add>%s
</add>""" % CMD_VLAN_ID

CMD_INT_VLAN_TRAILER = """
</vlan>
</allowed>
</trunk>
Expand All @@ -87,6 +93,14 @@
</interface>
"""

CMD_INT_VLAN_SNIPPET = (CMD_INT_VLAN_HEADER +
CMD_VLAN_ID +
CMD_INT_VLAN_TRAILER)

CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
CMD_VLAN_ADD_ID +
CMD_INT_VLAN_TRAILER)

CMD_PORT_TRUNK = """
<interface>
<ethernet>
Expand Down
122 changes: 122 additions & 0 deletions quantum/tests/unit/cisco/test_network_plugin.py
Expand Up @@ -13,25 +13,43 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import contextlib
import logging
import mock

from quantum.common import config
from quantum import context
from quantum.db import api as db
from quantum.manager import QuantumManager
from quantum.plugins.cisco.common import cisco_constants as const
from quantum.plugins.cisco.common import cisco_credentials_v2
from quantum.plugins.cisco.db import network_db_v2
from quantum.plugins.cisco.db import network_models_v2
from quantum.plugins.cisco import l2network_plugin_configuration
from quantum.plugins.cisco.models import virt_phy_sw_v2
from quantum.plugins.cisco.nexus import cisco_nexus_configuration
from quantum.plugins.openvswitch.common import config as ovs_config
from quantum.tests.unit import test_db_plugin

LOG = logging.getLogger(__name__)
DEVICE_ID_1 = '11111111-1111-1111-1111-111111111111'
DEVICE_ID_2 = '22222222-2222-2222-2222-222222222222'


class CiscoNetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase):

_plugin_name = 'quantum.plugins.cisco.network_plugin.PluginV2'
mock_ncclient = mock.Mock()

def setUp(self):
self.addCleanup(mock.patch.stopall)

# Use a mock netconf client
ncclient_patch = {
'ncclient': CiscoNetworkPluginV2TestCase.mock_ncclient
}
mock.patch.dict('sys.modules', ncclient_patch).start()

super(CiscoNetworkPluginV2TestCase, self).setUp(self._plugin_name)
self.port_create_status = 'DOWN'

Expand Down Expand Up @@ -60,6 +78,97 @@ class TestCiscoV2HTTPResponse(CiscoNetworkPluginV2TestCase,
class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
test_db_plugin.TestPortsV2):

def setUp(self):
"""Configure for end-to-end quantum testing using a mock ncclient.
This setup includes:
- Configure the OVS plugin to use VLANs in the range of 1000-1100.
- Configure the Cisco plugin model to use the real Nexus driver.
- Configure the Nexus sub-plugin to use an imaginary switch
at 1.1.1.1.
"""
self.addCleanup(mock.patch.stopall)

ovs_opts = {
'bridge_mappings': 'physnet1:br-eth1',
'network_vlan_ranges': ['physnet1:1000:1100'],
'tenant_network_type': 'vlan',
}
for opt in ovs_opts:
ovs_config.cfg.CONF.set_override(opt, ovs_opts[opt], 'OVS')
self.addCleanup(ovs_config.cfg.CONF.reset)

vswitch_plugin = ('quantum.plugins.openvswitch.'
'ovs_quantum_plugin.OVSQuantumPluginV2')
nexus_plugin = ('quantum.plugins.cisco.nexus.'
'cisco_nexus_plugin_v2.NexusPlugin')
nexus_driver = ('quantum.plugins.cisco.nexus.'
'cisco_nexus_network_driver_v2.CiscoNEXUSDriver')
switch_ip = '1.1.1.1'
nexus_config = {
switch_ip: {
'testhost': {'ports': '1/1'},
'ssh_port': {'ssh_port': 22},
}
}
nexus_creds = {
switch_ip: {
'username': 'admin',
'password': 'mySecretPassword',
}
}
mock.patch.dict(l2network_plugin_configuration.PLUGINS['PLUGINS'],
{
'vswitch_plugin': vswitch_plugin,
'nexus_plugin': nexus_plugin,
}).start()
mock.patch.object(cisco_nexus_configuration, 'NEXUS_DRIVER',
new=nexus_driver).start()
mock.patch.dict(cisco_nexus_configuration.CP['SWITCH'],
nexus_config).start()
mock.patch.dict(cisco_credentials_v2._creds_dictionary,
nexus_creds).start()

mock_sw = mock.patch.object(
virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
'_get_instance_host').start()
mock_sw.return_value = 'testhost'

super(TestCiscoPortsV2, self).setUp()

@contextlib.contextmanager
def _create_port_res(self, name='myname', cidr='1.0.0.0/24',
device_id=DEVICE_ID_1, do_delete=True):
"""Create a network, subnet, and port and yield the result.
Create a network, subnet, and port, yield the result,
then delete the port, subnet, and network.
:param name: Name of network to be created
:param cidr: cidr address of subnetwork to be created
:param device_id: Device ID to use for port to be created
:param do_delete: If set to True, delete the port at the
end of testing
"""
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=device_id)
port = self.deserialize(self.fmt, res)
try:
yield res
finally:
if do_delete:
self._delete('ports', port['port']['id'])

def _is_in_last_nexus_cfg(self, words):
last_cfg = (CiscoNetworkPluginV2TestCase.mock_ncclient.manager.
connect.return_value.edit_config.
mock_calls[-1][2]['config'])
return all(word in last_cfg for word in words)

def test_create_ports_bulk_emulated_plugin_failure(self):
real_has_attr = hasattr

Expand Down Expand Up @@ -109,6 +218,19 @@ def side_effect(*args, **kwargs):
# We expect a 500 as we injected a fault in the plugin
self._validate_behavior_on_bulk_failure(res, 'ports', 500)

def test_nexus_enable_vlan_cmd(self):
"""Verify the syntax of the command to enable a vlan on an intf."""
# First vlan should be configured without 'add' keyword
with self._create_port_res(name='net1', cidr='1.0.0.0/24',
device_id=DEVICE_ID_1):
self.assertTrue(self._is_in_last_nexus_cfg(['allowed', 'vlan']))
self.assertFalse(self._is_in_last_nexus_cfg(['add']))
# Second vlan should be configured with 'add' keyword
with self._create_port_res(name='net2', cidr='1.0.1.0/24',
device_id=DEVICE_ID_2):
self.assertTrue(
self._is_in_last_nexus_cfg(['allowed', 'vlan', 'add']))


class TestCiscoNetworksV2(CiscoNetworkPluginV2TestCase,
test_db_plugin.TestNetworksV2):
Expand Down

0 comments on commit b9daf57

Please sign in to comment.