diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..68c771a099 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/quantum/api/faults.py b/quantum/api/faults.py index ef4d50bc6b..670ef9f899 100644 --- a/quantum/api/faults.py +++ b/quantum/api/faults.py @@ -31,6 +31,7 @@ class Fault(webob.exc.HTTPException): 401: "unauthorized", 420: "networkNotFound", 421: "networkInUse", + 422: "networkNameExists", 430: "portNotFound", 431: "requestedStateInvalid", 432: "portInUse", @@ -91,6 +92,22 @@ class NetworkInUse(webob.exc.HTTPClientError): explanation = ('Unable to remove the network: attachments still plugged.') +class NetworkNameExists(webob.exc.HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the server could not set the network name to the + specified value because another network for the same tenant already has + that name. + + code: 422, title: Network Name Exists + """ + code = 422 + title = 'Network Name Exists' + explanation = ('Unable to set network name: tenant already has network' \ + ' with same name.') + + class PortNotFound(webob.exc.HTTPClientError): """ subclass of :class:`~HTTPClientError` diff --git a/quantum/api/networks.py b/quantum/api/networks.py index 9afc09c24e..27709eb594 100644 --- a/quantum/api/networks.py +++ b/quantum/api/networks.py @@ -107,12 +107,15 @@ class Controller(common.QuantumController): self._network_ops_param_list) except exc.HTTPError as e: return faults.Fault(e) - network = self._plugin.\ + try: + network = self._plugin.\ create_network(tenant_id, request_params['net-name']) - builder = networks_view.get_view_builder(request) - result = builder.build(network) - return dict(networks=result) + builder = networks_view.get_view_builder(request) + result = builder.build(network) + return dict(networks=result) + except exception.NetworkNameExists as e: + return faults.Fault(faults.NetworkNameExists(e)) def update(self, request, tenant_id, id): """ Updates the name for the network with the given id """ @@ -128,6 +131,8 @@ class Controller(common.QuantumController): return exc.HTTPAccepted() except exception.NetworkNotFound as e: return faults.Fault(faults.NetworkNotFound(e)) + except exception.NetworkNameExists as e: + return faults.Fault(faults.NetworkNameExists(e)) def delete(self, request, tenant_id, id): """ Destroys the network with the given id """ diff --git a/quantum/common/exceptions.py b/quantum/common/exceptions.py index 4daf31762b..478ddd551b 100644 --- a/quantum/common/exceptions.py +++ b/quantum/common/exceptions.py @@ -21,6 +21,9 @@ Quantum-type exceptions. SHOULD include dedicated exception logging. """ import logging +import gettext + +gettext.install('quantum', unicode=1) class QuantumException(Exception): @@ -108,6 +111,12 @@ class AlreadyAttached(QuantumException): "already plugged into port %(att_port_id)s") +class NetworkNameExists(QuantumException): + message = _("Unable to set network name to %(net_name). " \ + "Network with id %(net_id) already has this name for " \ + "tenant %(tenant_id)") + + class Duplicate(Error): pass diff --git a/quantum/db/api.py b/quantum/db/api.py index 2b7167ba8d..8153bd9dae 100644 --- a/quantum/db/api.py +++ b/quantum/db/api.py @@ -20,7 +20,7 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, exc - +from quantum.common import exceptions as q_exc from quantum.db import models @@ -77,21 +77,29 @@ def unregister_models(): BASE.metadata.drop_all(_ENGINE) -def network_create(tenant_id, name): +def _check_duplicate_net_name(tenant_id, net_name): session = get_session() - net = None try: net = session.query(models.Network).\ - filter_by(tenant_id=tenant_id, name=name).\ + filter_by(tenant_id=tenant_id, name=net_name).\ one() - raise Exception("Network with name %(name)s already " \ - "exists for tenant %(tenant_id)s" % locals()) + raise q_exc.NetworkNameExists(tenant_id=tenant_id, + net_name=net_name, net_id=net.uuid) except exc.NoResultFound: - with session.begin(): - net = models.Network(tenant_id, name) - session.add(net) - session.flush() - return net + # this is the "normal" path, as API spec specifies + # that net-names are unique within a tenant + pass + + +def network_create(tenant_id, name): + session = get_session() + + _check_duplicate_net_name(tenant_id, name) + with session.begin(): + net = models.Network(tenant_id, name) + session.add(net) + session.flush() + return net def network_list(tenant_id): @@ -107,23 +115,18 @@ def network_get(net_id): return session.query(models.Network).\ filter_by(uuid=net_id).\ one() - except exc.NoResultFound: - raise Exception("No net found with id = %s" % net_id) + except exc.NoResultFound, e: + raise q_exc.NetworkNotFound(net_id=net_id) def network_rename(net_id, tenant_id, new_name): session = get_session() - try: - res = session.query(models.Network).\ - filter_by(tenant_id=tenant_id, name=new_name).\ - one() - except exc.NoResultFound: - net = network_get(net_id) - net.name = new_name - session.merge(net) - session.flush() - return net - raise Exception("A network with name \"%s\" already exists" % new_name) + net = network_get(net_id) + _check_duplicate_net_name(tenant_id, new_name) + net.name = new_name + session.merge(net) + session.flush() + return net def network_destroy(net_id): @@ -136,10 +139,13 @@ def network_destroy(net_id): session.flush() return net except exc.NoResultFound: - raise Exception("No network found with id = %s" % net_id) + raise q_exc.NetworkNotFound(net_id=net_id) def port_create(net_id, state=None): + # confirm network exists + network_get(net_id) + session = get_session() with session.begin(): port = models.Port(net_id) @@ -156,63 +162,90 @@ def port_list(net_id): all() -def port_get(port_id): +def port_get(port_id, net_id): + # confirm network exists + network_get(net_id) session = get_session() try: return session.query(models.Port).\ filter_by(uuid=port_id).\ + filter_by(network_id=net_id).\ one() except exc.NoResultFound: - raise Exception("No port found with id = %s " % port_id) + raise q_exc.PortNotFound(net_id=net_id, port_id=port_id) -def port_set_state(port_id, new_state): - port = port_get(port_id) - if port: - session = get_session() - port.state = new_state - session.merge(port) - session.flush() - return port +def port_set_state(port_id, net_id, new_state): + if new_state not in ('ACTIVE', 'DOWN'): + raise q_exc.StateInvalid(port_state=new_state) + # confirm network exists + network_get(net_id) -def port_set_attachment(port_id, new_interface_id): + port = port_get(port_id, net_id) session = get_session() - ports = [] + port.state = new_state + session.merge(port) + session.flush() + return port + + +def port_set_attachment(port_id, net_id, new_interface_id): + # confirm network exists + network_get(net_id) + + session = get_session() + port = port_get(port_id, net_id) + if new_interface_id != "": + # We are setting, not clearing, the attachment-id + if port['interface_id']: + raise q_exc.PortInUse(net_id=net_id, port_id=port_id, + att_id=port['interface_id']) + try: - ports = session.query(models.Port).\ + port = session.query(models.Port).\ filter_by(interface_id=new_interface_id).\ - all() + one() + raise q_exc.AlreadyAttached(net_id=net_id, + port_id=port_id, + att_id=new_interface_id, + att_port_id=port['uuid']) except exc.NoResultFound: + # this is what should happen pass - if len(ports) == 0: - port = port_get(port_id) - port.interface_id = new_interface_id - session.merge(port) - session.flush() - return port - else: - raise Exception("Port with attachment \"%s\" already exists" - % (new_interface_id)) + port.interface_id = new_interface_id + session.merge(port) + session.flush() + return port -def port_unset_attachment(port_id): +def port_unset_attachment(port_id, net_id): + # confirm network exists + network_get(net_id) + session = get_session() - port = port_get(port_id) + port = port_get(port_id, net_id) port.interface_id = None session.merge(port) - session.flush + session.flush() -def port_destroy(port_id): +def port_destroy(port_id, net_id): + # confirm network exists + network_get(net_id) + session = get_session() try: port = session.query(models.Port).\ - filter_by(uuid=port_id).\ - one() + filter_by(uuid=port_id).\ + filter_by(network_id=net_id).\ + one() + if port['interface_id']: + raise q_exc.PortInUse(net_id=net_id, port_id=port_id, + att_id=port['interface_id']) session.delete(port) session.flush() return port except exc.NoResultFound: - raise Exception("No port found with id = %s " % port_id) + raise q_exc.PortNotFound(port_id=port_id) diff --git a/quantum/plugins/SamplePlugin.py b/quantum/plugins/SamplePlugin.py index 313ac8e1db..8fa701dd68 100644 --- a/quantum/plugins/SamplePlugin.py +++ b/quantum/plugins/SamplePlugin.py @@ -245,7 +245,7 @@ class FakePlugin(object): def _get_port(self, tenant_id, network_id, port_id): net = self._get_network(tenant_id, network_id) try: - port = db.port_get(port_id) + port = db.port_get(port_id, network_id) except: raise exc.PortNotFound(net_id=network_id, port_id=port_id) # Port must exist and belong to the appropriate network. @@ -327,10 +327,7 @@ class FakePlugin(object): Virtual Network. """ LOG.debug("FakePlugin.rename_network() called") - try: - db.network_rename(net_id, tenant_id, new_name) - except: - raise exc.NetworkNotFound(net_id=net_id) + db.network_rename(net_id, tenant_id, new_name) net = self._get_network(tenant_id, net_id) return net @@ -378,7 +375,7 @@ class FakePlugin(object): self._get_network(tenant_id, net_id) self._get_port(tenant_id, net_id, port_id) self._validate_port_state(new_state) - db.port_set_state(port_id, new_state) + db.port_set_state(port_id, net_id, new_state) port_item = {'port-id': port_id, 'port-state': new_state} return port_item @@ -397,7 +394,7 @@ class FakePlugin(object): raise exc.PortInUse(net_id=net_id, port_id=port_id, att_id=port['interface_id']) try: - port = db.port_destroy(port_id) + port = db.port_destroy(port_id, net_id) except Exception, e: raise Exception("Failed to delete port: %s" % str(e)) d = {} @@ -417,7 +414,7 @@ class FakePlugin(object): if port['interface_id']: raise exc.PortInUse(net_id=net_id, port_id=port_id, att_id=port['interface_id']) - db.port_set_attachment(port_id, remote_interface_id) + db.port_set_attachment(port_id, net_id, remote_interface_id) def unplug_interface(self, tenant_id, net_id, port_id): """ @@ -428,4 +425,4 @@ class FakePlugin(object): self._get_port(tenant_id, net_id, port_id) # TODO(salvatore-orlando): # Should unplug on port without attachment raise an Error? - db.port_unset_attachment(port_id) + db.port_unset_attachment(port_id, net_id) diff --git a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py index 5d2a66ca37..404c801787 100755 --- a/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py +++ b/quantum/plugins/openvswitch/agent/ovs_quantum_agent.py @@ -198,7 +198,7 @@ class OVSQuantumAgent: while True: cursor = conn.cursor() - cursor.execute("SELECT * FROM ports") + cursor.execute("SELECT * FROM ports where state = 'ACTIVE'") rows = cursor.fetchall() cursor.close() all_bindings = {} diff --git a/quantum/plugins/openvswitch/ovs_quantum_plugin.py b/quantum/plugins/openvswitch/ovs_quantum_plugin.py index 2f23517ec7..1b46fca7c1 100644 --- a/quantum/plugins/openvswitch/ovs_quantum_plugin.py +++ b/quantum/plugins/openvswitch/ovs_quantum_plugin.py @@ -19,12 +19,13 @@ import ConfigParser import logging as LOG +from optparse import OptionParser import os import sys import unittest +from quantum.common import exceptions as q_exc from quantum.quantum_plugin_base import QuantumPluginBase -from optparse import OptionParser import quantum.db.api as db import ovs_db @@ -111,101 +112,93 @@ class OVSQuantumPlugin(QuantumPluginBase): nets = [] for x in db.network_list(tenant_id): LOG.debug("Adding network: %s" % x.uuid) - d = {} - d["net-id"] = str(x.uuid) - d["net-name"] = x.name - nets.append(d) + nets.append(self._make_net_dict(str(x.uuid), x.name, None)) return nets + def _make_net_dict(self, net_id, net_name, ports): + res = {'net-id': net_id, + 'net-name': net_name} + if ports: + res['net-ports'] = ports + return res + def create_network(self, tenant_id, net_name): - d = {} - try: - res = db.network_create(tenant_id, net_name) - LOG.debug("Created newtork: %s" % res) - except Exception, e: - LOG.error("Error: %s" % str(e)) - return d - d["net-id"] = str(res.uuid) - d["net-name"] = res.name - vlan_id = self.vmap.acquire(str(res.uuid)) - ovs_db.add_vlan_binding(vlan_id, str(res.uuid)) - return d + net = db.network_create(tenant_id, net_name) + LOG.debug("Created network: %s" % net) + vlan_id = self.vmap.acquire(str(net.uuid)) + ovs_db.add_vlan_binding(vlan_id, str(net.uuid)) + return self._make_net_dict(str(net.uuid), net.name, []) def delete_network(self, tenant_id, net_id): + net = db.network_get(net_id) + + # Verify that no attachments are plugged into the network + for port in db.port_list(net_id): + if port['interface_id']: + raise q_exc.NetworkInUse(net_id=net_id) net = db.network_destroy(net_id) - d = {} - d["net-id"] = str(net.uuid) ovs_db.remove_vlan_binding(net_id) self.vmap.release(net_id) - return d + return self._make_net_dict(str(net.uuid), net.name, []) def get_network_details(self, tenant_id, net_id): - ports = db.port_list(net_id) - ifaces = [] - for p in ports: - ifaces.append(p.interface_id) - return ifaces + net = db.network_get(net_id) + ports = self.get_all_ports(tenant_id, net_id) + return self._make_net_dict(str(net.uuid), net.name, ports) def rename_network(self, tenant_id, net_id, new_name): - try: - net = db.network_rename(net_id, tenant_id, new_name) - except Exception, e: - raise Exception("Failed to rename network: %s" % str(e)) - d = {} - d["net-id"] = str(net.uuid) - d["net-name"] = net.name - return d + net = db.network_rename(net_id, tenant_id, new_name) + return self._make_net_dict(str(net.uuid), net.name, None) + + def _make_port_dict(self, port_id, port_state, net_id, attachment): + res = {'port-id': port_id, + 'port-state': port_state} + if net_id: + res['net-id'] = net_id + if attachment: + res['attachment-id'] = attachment + return res def get_all_ports(self, tenant_id, net_id): ids = [] ports = db.port_list(net_id) - for x in ports: - LOG.debug("Appending port: %s" % x.uuid) - d = {} - d["port-id"] = str(x.uuid) + for p in ports: + LOG.debug("Appending port: %s" % p.uuid) + d = self._make_port_dict(str(p.uuid), p.state, None, None) ids.append(d) return ids def create_port(self, tenant_id, net_id, port_state=None): LOG.debug("Creating port with network_id: %s" % net_id) - port = db.port_create(net_id) - d = {} - d["port-id"] = str(port.uuid) - LOG.debug("-> %s" % (port.uuid)) - return d + port = db.port_create(net_id, port_state) + return self._make_port_dict(str(port.uuid), port.state, None, None) def delete_port(self, tenant_id, net_id, port_id): - try: - port = db.port_destroy(port_id) - except Exception, e: - raise Exception("Failed to delete port: %s" % str(e)) - d = {} - d["port-id"] = str(port.uuid) - return d + port = db.port_destroy(port_id, net_id) + return self._make_port_dict(str(port.uuid), port.state, None, None) def update_port(self, tenant_id, net_id, port_id, port_state): """ Updates the state of a port on the specified Virtual Network. """ LOG.debug("update_port() called\n") - port = db.port_get(port_id) - port['port-state'] = port_state - return port + port = db.port_get(port_id, net_id) + db.port_set_state(port_id, net_id, port_state) + return self._make_port_dict(str(port.uuid), port.state, None, None) def get_port_details(self, tenant_id, net_id, port_id): - port = db.port_get(port_id) - rv = {"port-id": port.uuid, "attachment": port.interface_id, - "net-id": port.network_id, "port-state": "UP"} - return rv + port = db.port_get(port_id, net_id) + return self._make_port_dict(str(port.uuid), port.state, + port.network_id, port.interface_id) def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id): - db.port_set_attachment(port_id, remote_iface_id) + db.port_set_attachment(port_id, net_id, remote_iface_id) def unplug_interface(self, tenant_id, net_id, port_id): - db.port_set_attachment(port_id, "") + db.port_set_attachment(port_id, net_id, "") def get_interface_details(self, tenant_id, net_id, port_id): - res = db.port_get(port_id) + res = db.port_get(port_id, net_id) return res.interface_id @@ -227,108 +220,6 @@ class VlanMapTest(unittest.TestCase): self.assertTrue(self.vmap.get(vlan_id) == None) -# TODO(bgh): Make the tests use a sqlite database instead of mysql -class OVSPluginTest(unittest.TestCase): - - def setUp(self): - self.quantum = OVSQuantumPlugin() - self.tenant_id = "testtenant" - - def testCreateNetwork(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - self.assertTrue(net1["net-name"] == "plugin_test1") - - def testGetNetworks(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - net2 = self.quantum.create_network(self.tenant_id, "plugin_test2") - nets = self.quantum.get_all_networks(self.tenant_id) - count = 0 - for x in nets: - if "plugin_test" in x["net-name"]: - count += 1 - self.assertTrue(count == 2) - - def testDeleteNetwork(self): - net = self.quantum.create_network(self.tenant_id, "plugin_test1") - self.quantum.delete_network(self.tenant_id, net["net-id"]) - nets = self.quantum.get_all_networks(self.tenant_id) - count = 0 - for x in nets: - if "plugin_test" in x["net-name"]: - count += 1 - self.assertTrue(count == 0) - - def testRenameNetwork(self): - net = self.quantum.create_network(self.tenant_id, "plugin_test1") - net = self.quantum.rename_network(self.tenant_id, net["net-id"], - "plugin_test_renamed") - self.assertTrue(net["net-name"] == "plugin_test_renamed") - - def testCreatePort(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - port = self.quantum.create_port(self.tenant_id, net1["net-id"]) - ports = self.quantum.get_all_ports(self.tenant_id, net1["net-id"]) - count = 0 - for p in ports: - count += 1 - self.assertTrue(count == 1) - - def testDeletePort(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - port = self.quantum.create_port(self.tenant_id, net1["net-id"]) - ports = self.quantum.get_all_ports(self.tenant_id, net1["net-id"]) - count = 0 - for p in ports: - count += 1 - self.assertTrue(count == 1) - for p in ports: - self.quantum.delete_port(self.tenant_id, id, p["port-id"]) - ports = self.quantum.get_all_ports(self.tenant_id, net1["net-id"]) - count = 0 - for p in ports: - count += 1 - self.assertTrue(count == 0) - - def testGetPorts(self): - pass - - def testPlugInterface(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - port = self.quantum.create_port(self.tenant_id, net1["net-id"]) - self.quantum.plug_interface(self.tenant_id, net1["net-id"], - port["port-id"], "vif1.1") - port = self.quantum.get_port_details(self.tenant_id, net1["net-id"], - port["port-id"]) - self.assertTrue(port["attachment"] == "vif1.1") - - def testUnPlugInterface(self): - net1 = self.quantum.create_network(self.tenant_id, "plugin_test1") - port = self.quantum.create_port(self.tenant_id, net1["net-id"]) - self.quantum.plug_interface(self.tenant_id, net1["net-id"], - port["port-id"], "vif1.1") - port = self.quantum.get_port_details(self.tenant_id, net1["net-id"], - port["port-id"]) - self.assertTrue(port["attachment"] == "vif1.1") - self.quantum.unplug_interface(self.tenant_id, net1["net-id"], - port["port-id"]) - port = self.quantum.get_port_details(self.tenant_id, net1["net-id"], - port["port-id"]) - self.assertTrue(port["attachment"] == "") - - def tearDown(self): - networks = self.quantum.get_all_networks(self.tenant_id) - # Clean up any test networks lying around - for net in networks: - id = net["net-id"] - name = net["net-name"] - if "plugin_test" in name: - # Clean up any test ports lying around - ports = self.quantum.get_all_ports(self.tenant_id, id) - for p in ports: - self.quantum.delete_port(self.tenant_id, id, p["port-id"]) - self.quantum.delete_network(self.tenant_id, id) - - if __name__ == "__main__": usagestr = "Usage: %prog [OPTIONS] [args]" parser = OptionParser(usage=usagestr) @@ -345,7 +236,5 @@ if __name__ == "__main__": # Make sqlalchemy quieter LOG.getLogger('sqlalchemy.engine').setLevel(LOG.WARN) # Run the tests - suite = unittest.TestLoader().loadTestsFromTestCase(OVSPluginTest) - unittest.TextTestRunner(verbosity=2).run(suite) suite = unittest.TestLoader().loadTestsFromTestCase(VlanMapTest) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/unit/test_api.py b/tests/unit/test_api.py index 513afccb84..aee8f73f3e 100644 --- a/tests/unit/test_api.py +++ b/tests/unit/test_api.py @@ -150,6 +150,19 @@ class APITest(unittest.TestCase): network_data['network']) LOG.debug("_test_rename_network - format:%s - END", format) + def _test_rename_network_duplicate(self, format): + LOG.debug("_test_rename_network_duplicate - format:%s - START", format) + content_type = "application/%s" % format + network_id1 = self._create_network(format, name="net1") + network_id2 = self._create_network(format, name="net2") + update_network_req = testlib.update_network_request(self.tenant_id, + network_id2, + "net1", + format) + update_network_res = update_network_req.get_response(self.api) + self.assertEqual(update_network_res.status_int, 422) + LOG.debug("_test_rename_network_duplicate - format:%s - END", format) + def _test_rename_network_badrequest(self, format): LOG.debug("_test_rename_network_badrequest - format:%s - START", format) @@ -427,6 +440,23 @@ class APITest(unittest.TestCase): show_port_res.body, content_type) self.assertEqual({'id': port_id, 'state': new_port_state}, port_data['port']) + + # now set it back to the original value + update_port_req = testlib.update_port_request(self.tenant_id, + network_id, port_id, + port_state, + format) + update_port_res = update_port_req.get_response(self.api) + self.assertEqual(update_port_res.status_int, 200) + show_port_req = testlib.show_port_request(self.tenant_id, + network_id, port_id, + format) + show_port_res = show_port_req.get_response(self.api) + self.assertEqual(show_port_res.status_int, 200) + port_data = self._port_serializer.deserialize( + show_port_res.body, content_type) + self.assertEqual({'id': port_id, 'state': port_state}, + port_data['port']) LOG.debug("_test_set_port_state - format:%s - END", format) def _test_set_port_state_networknotfound(self, format): @@ -706,6 +736,12 @@ class APITest(unittest.TestCase): def test_rename_network_xml(self): self._test_rename_network('xml') + def test_rename_network_duplicate_json(self): + self._test_rename_network_duplicate('json') + + def test_rename_network_duplicate_xml(self): + self._test_rename_network_duplicate('xml') + def test_rename_network_badrequest_json(self): self._test_rename_network_badrequest('json') diff --git a/tools/pip-requires b/tools/pip-requires index baf065da29..31b66c77fe 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -3,7 +3,7 @@ Routes>=1.12.3 nose Paste PasteDeploy -pep8==0.5.0 +pep8>=0.5.0 python-gflags simplejson sqlalchemy