summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSalvatore Orlando <salvatore.orlando@eu.citrix.com>2011-12-05 17:28:33 +0000
committerSalvatore Orlando <salvatore.orlando@eu.citrix.com>2011-12-13 10:51:44 +0000
commit6e38dcb70ebdf7eae955402908c62f0ae048ccf6 (patch)
treea90fd2eba6e1fde8a8d42018d9a3765c489f96d6
parent2ccf22e98a74987ca2ddab88139a0e5f1d9d45af (diff)
downloadneutron-6e38dcb70ebdf7eae955402908c62f0ae048ccf6.tar.gz
blueprint api-operational-status
Adds a new attribute expressing current operational status for port and network resources Also includes: - db models: changes to accomodate operational status concept - unit tests: changes to include different validation functions for API v1.0 and v.1.1 This changeset does not include changes to the client library NOTE: Addressing issue concerning unit tests for OVS plugin. API unit tests launched with PLUGIN_DIR set to Cisco's plugin work fine as well. Change-Id: I0c4f0f8a8c392bae75c668c28070364ca230f965
-rw-r--r--quantum/api/api_common.py16
-rw-r--r--quantum/api/networks.py12
-rw-r--r--quantum/api/ports.py8
-rw-r--r--quantum/api/views/networks.py28
-rw-r--r--quantum/api/views/ports.py26
-rw-r--r--quantum/common/test_lib.py2
-rw-r--r--quantum/db/api.py9
-rw-r--r--quantum/db/models.py22
-rw-r--r--quantum/plugins/openvswitch/run_tests.py5
-rw-r--r--quantum/plugins/sample/SamplePlugin.py16
-rw-r--r--quantum/tests/unit/_test_api.py40
-rw-r--r--quantum/tests/unit/test_api.py55
12 files changed, 190 insertions, 49 deletions
diff --git a/quantum/api/api_common.py b/quantum/api/api_common.py
index d3a6c97455..96fe70a208 100644
--- a/quantum/api/api_common.py
+++ b/quantum/api/api_common.py
@@ -27,6 +27,22 @@ XML_NS_V11 = 'http://openstack.org/quantum/api/v1.1'
LOG = logging.getLogger('quantum.api.api_common')
+class OperationalStatus:
+ """ Enumeration for operational status
+
+ UP : the resource is available (operationall up)
+ DOWN : the resource is not operational; this might indicate
+ a failure in the underlying switching fabric.
+ PROVISIONING: the plugin is creating or updating the resource
+ in the underlying switching fabric
+ UNKNOWN: the plugin does not support the operational status concept.
+ """
+ UP = "UP"
+ DOWN = "DOWN"
+ PROVISIONING = "PROVISIONING"
+ UNKNOWN = "UNKNOWN"
+
+
def create_resource(version, controller_dict):
"""
Generic function for creating a wsgi resource
diff --git a/quantum/api/networks.py b/quantum/api/networks.py
index a770c24d1f..50b1e0aa71 100644
--- a/quantum/api/networks.py
+++ b/quantum/api/networks.py
@@ -146,12 +146,18 @@ class ControllerV10(Controller):
class ControllerV11(Controller):
- """Network resources controller for Quantum v1.1 API"""
+ """Network resources controller for Quantum v1.1 API
+
+ Note: at this state this class only adds serialization
+ metadata for the operational status concept.
+ API filters, pagination, and atom links will be handled by
+ this class as well.
+ """
_serialization_metadata = {
"attributes": {
- "network": ["id", "name"],
- "port": ["id", "state"],
+ "network": ["id", "name", "op-status"],
+ "port": ["id", "state", "op-status"],
"attachment": ["id"]},
"plurals": {"networks": "network",
"ports": "port"}
diff --git a/quantum/api/ports.py b/quantum/api/ports.py
index d0d69f52d3..fb5a578646 100644
--- a/quantum/api/ports.py
+++ b/quantum/api/ports.py
@@ -50,7 +50,7 @@ class Controller(common.QuantumController):
port_details=False):
""" Returns a list of ports. """
port_list = self._plugin.get_all_ports(tenant_id, network_id)
- builder = ports_view.get_view_builder(request)
+ builder = ports_view.get_view_builder(request, self.version)
# Load extra data for ports if required.
if port_details:
@@ -69,7 +69,7 @@ class Controller(common.QuantumController):
""" Returns a specific port. """
port = self._plugin.get_port_details(
tenant_id, network_id, port_id)
- builder = ports_view.get_view_builder(request)
+ builder = ports_view.get_view_builder(request, self.version)
result = builder.build(port, port_details=True,
att_details=att_details)['port']
return dict(port=result)
@@ -111,7 +111,7 @@ class Controller(common.QuantumController):
port = self._plugin.create_port(tenant_id,
network_id, body['port']['state'],
**body)
- builder = ports_view.get_view_builder(request)
+ builder = ports_view.get_view_builder(request, self.version)
result = builder.build(port)['port']
return dict(port=result)
@@ -151,7 +151,7 @@ class ControllerV11(Controller):
_serialization_metadata = {
"attributes": {
- "port": ["id", "state"],
+ "port": ["id", "state", "op-status"],
"attachment": ["id"]},
"plurals": {"ports": "port"}
}
diff --git a/quantum/api/views/networks.py b/quantum/api/views/networks.py
index 1ed858e50a..40b0b35bde 100644
--- a/quantum/api/views/networks.py
+++ b/quantum/api/views/networks.py
@@ -15,6 +15,8 @@
# License for the specific language governing permissions and limitations
# under the License.
+from quantum.api.api_common import OperationalStatus
+
def get_view_builder(req, version):
base_url = req.application_url
@@ -64,6 +66,26 @@ class ViewBuilder10(object):
class ViewBuilder11(ViewBuilder10):
- #TODO(salvatore-orlando): will extend for Operational status
- # in appropriate branch
- pass
+
+ def _build_simple(self, network_data):
+ """Return a simple model of a network."""
+ return dict(network=dict(id=network_data['net-id']))
+
+ def _build_detail(self, network_data):
+ """Return a detailed model of a network. """
+ op_status = network_data.get('net-op-status',
+ OperationalStatus.UNKNOWN)
+ return dict(network={'id': network_data['net-id'],
+ 'name': network_data['net-name'],
+ 'op-status': op_status})
+
+ def _build_port(self, port_data):
+ """Return details about a specific logical port."""
+ op_status = port_data.get('port-op-status',
+ OperationalStatus.UNKNOWN)
+ port_dict = {'id': port_data['port-id'],
+ 'state': port_data['port-state'],
+ 'op-status': op_status}
+ if port_data['attachment']:
+ port_dict['attachment'] = dict(id=port_data['attachment'])
+ return port_dict
diff --git a/quantum/api/views/ports.py b/quantum/api/views/ports.py
index dd3f18ea23..919e9f5c49 100644
--- a/quantum/api/views/ports.py
+++ b/quantum/api/views/ports.py
@@ -15,13 +15,19 @@
# License for the specific language governing permissions and limitations
# under the License.
+from quantum.api.api_common import OperationalStatus
-def get_view_builder(req):
+
+def get_view_builder(req, version):
base_url = req.application_url
- return ViewBuilder(base_url)
+ view_builder = {
+ '1.0': ViewBuilder10,
+ '1.1': ViewBuilder11,
+ }[version](base_url)
+ return view_builder
-class ViewBuilder(object):
+class ViewBuilder10(object):
def __init__(self, base_url=None):
"""
@@ -37,3 +43,17 @@ class ViewBuilder(object):
if att_details and port_data['attachment']:
port['port']['attachment'] = dict(id=port_data['attachment'])
return port
+
+
+class ViewBuilder11(ViewBuilder10):
+
+ def build(self, port_data, port_details=False, att_details=False):
+ """Generates a port entity with operation status info"""
+ port = dict(port=dict(id=port_data['port-id']))
+ if port_details:
+ port['port']['state'] = port_data['port-state']
+ port['port']['op-status'] = port_data.get('port-op-status',
+ OperationalStatus.UNKNOWN)
+ if att_details and port_data['attachment']:
+ port['port']['attachment'] = dict(id=port_data['attachment'])
+ return port
diff --git a/quantum/common/test_lib.py b/quantum/common/test_lib.py
index 9df0574c7b..03578817d6 100644
--- a/quantum/common/test_lib.py
+++ b/quantum/common/test_lib.py
@@ -286,4 +286,6 @@ def run_tests(c=None):
# quantum/plugins/openvswitch/ )
test_config = {
"plugin_name": "quantum.plugins.sample.SamplePlugin.FakePlugin",
+ "default_net_op_status": "UP",
+ "default_port_op_status": "UP",
}
diff --git a/quantum/db/api.py b/quantum/db/api.py
index d262f8acf1..84d5b9f53f 100644
--- a/quantum/db/api.py
+++ b/quantum/db/api.py
@@ -22,6 +22,7 @@ import logging
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, exc
+from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as q_exc
from quantum.db import models
@@ -80,11 +81,11 @@ def unregister_models():
BASE.metadata.drop_all(_ENGINE)
-def network_create(tenant_id, name):
+def network_create(tenant_id, name, op_status=OperationalStatus.UNKNOWN):
session = get_session()
with session.begin():
- net = models.Network(tenant_id, name)
+ net = models.Network(tenant_id, name, op_status)
session.add(net)
session.flush()
return net
@@ -137,13 +138,13 @@ def network_destroy(net_id):
raise q_exc.NetworkNotFound(net_id=net_id)
-def port_create(net_id, state=None):
+def port_create(net_id, state=None, op_status=OperationalStatus.UNKNOWN):
# confirm network exists
network_get(net_id)
session = get_session()
with session.begin():
- port = models.Port(net_id)
+ port = models.Port(net_id, op_status)
port['state'] = state or 'DOWN'
session.add(port)
session.flush()
diff --git a/quantum/db/models.py b/quantum/db/models.py
index 409e91dea1..1039cbe219 100644
--- a/quantum/db/models.py
+++ b/quantum/db/models.py
@@ -16,14 +16,15 @@
# @author: Somik Behera, Nicira Networks, Inc.
# @author: Brad Hall, Nicira Networks, Inc.
# @author: Dan Wendlandt, Nicira Networks, Inc.
+# @author: Salvatore Orlando, Citrix Systems
import uuid
-
from sqlalchemy import Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation, object_mapper
+from quantum.api import api_common as common
BASE = declarative_base()
@@ -73,16 +74,20 @@ class Port(BASE, QuantumBase):
interface_id = Column(String(255), nullable=True)
# Port state - Hardcoding string value at the moment
state = Column(String(8))
+ op_status = Column(String(16))
- def __init__(self, network_id):
+ def __init__(self, network_id,
+ op_status=common.OperationalStatus.UNKNOWN):
self.uuid = str(uuid.uuid4())
self.network_id = network_id
self.interface_id = None
self.state = "DOWN"
+ self.op_status = op_status
def __repr__(self):
- return "<Port(%s,%s,%s,%s)>" % (self.uuid, self.network_id,
- self.state, self.interface_id)
+ return "<Port(%s,%s,%s,%s,%s)>" % (self.uuid, self.network_id,
+ self.state, self.op_status,
+ self.interface_id)
class Network(BASE, QuantumBase):
@@ -93,12 +98,15 @@ class Network(BASE, QuantumBase):
tenant_id = Column(String(255), nullable=False)
name = Column(String(255))
ports = relation(Port, order_by=Port.uuid, backref="network")
+ op_status = Column(String(16))
- def __init__(self, tenant_id, name):
+ def __init__(self, tenant_id, name,
+ op_status=common.OperationalStatus.UNKNOWN):
self.uuid = str(uuid.uuid4())
self.tenant_id = tenant_id
self.name = name
+ self.op_status = op_status
def __repr__(self):
- return "<Network(%s,%s,%s)>" % \
- (self.uuid, self.name, self.tenant_id)
+ return "<Network(%s,%s,%s,%s)>" % \
+ (self.uuid, self.name, self.op_status, self.tenant_id)
diff --git a/quantum/plugins/openvswitch/run_tests.py b/quantum/plugins/openvswitch/run_tests.py
index 0377dd8943..9649efa0d8 100644
--- a/quantum/plugins/openvswitch/run_tests.py
+++ b/quantum/plugins/openvswitch/run_tests.py
@@ -49,7 +49,12 @@ if __name__ == '__main__':
# we should only invoked the tests once
invoke_once = len(sys.argv) > 1
+ # NOTE (salvatore-orlando):
+ # please update default values for operational status according to
+ # the plugin behavior.
test_config['plugin_name'] = "ovs_quantum_plugin.OVSQuantumPlugin"
+ test_config['default_net_op_status'] = "UNKNOWN"
+ test_config['default_port_op_status'] = "UNKNOWN"
cwd = os.getcwd()
c = config.Config(stream=sys.stdout,
diff --git a/quantum/plugins/sample/SamplePlugin.py b/quantum/plugins/sample/SamplePlugin.py
index 2f5626609d..5fb6cbc812 100644
--- a/quantum/plugins/sample/SamplePlugin.py
+++ b/quantum/plugins/sample/SamplePlugin.py
@@ -18,6 +18,7 @@
import logging
+from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as exc
from quantum.db import api as db
@@ -174,7 +175,8 @@ class FakePlugin(object):
nets = []
for net in db.network_list(tenant_id):
net_item = {'net-id': str(net.uuid),
- 'net-name': net.name}
+ 'net-name': net.name,
+ 'net-op-status': net.op_status}
nets.append(net_item)
return nets
@@ -189,6 +191,7 @@ class FakePlugin(object):
ports = self.get_all_ports(tenant_id, net_id)
return {'net-id': str(net.uuid),
'net-name': net.name,
+ 'net-op-status': net.op_status,
'net-ports': ports}
def create_network(self, tenant_id, net_name, **kwargs):
@@ -198,8 +201,11 @@ class FakePlugin(object):
"""
LOG.debug("FakePlugin.create_network() called")
new_net = db.network_create(tenant_id, net_name)
+ # Put operational status UP
+ db.network_update(new_net.uuid, net_name,
+ op_status=OperationalStatus.UP)
# Return uuid for newly created network as net-id.
- return {'net-id': new_net['uuid']}
+ return {'net-id': new_net.uuid}
def delete_network(self, tenant_id, net_id):
"""
@@ -248,7 +254,8 @@ class FakePlugin(object):
port = self._get_port(tenant_id, net_id, port_id)
return {'port-id': str(port.uuid),
'attachment': port.interface_id,
- 'port-state': port.state}
+ 'port-state': port.state,
+ 'port-op-status': port.op_status}
def create_port(self, tenant_id, net_id, port_state=None, **kwargs):
"""
@@ -258,6 +265,9 @@ class FakePlugin(object):
# verify net_id
self._get_network(tenant_id, net_id)
port = db.port_create(net_id, port_state)
+ # Put operational status UP
+ db.port_update(port.uuid, net_id,
+ op_status=OperationalStatus.UP)
port_item = {'port-id': str(port.uuid)}
return port_item
diff --git a/quantum/tests/unit/_test_api.py b/quantum/tests/unit/_test_api.py
index d021c82f85..629c246b44 100644
--- a/quantum/tests/unit/_test_api.py
+++ b/quantum/tests/unit/_test_api.py
@@ -157,9 +157,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_network_res.status_int, 200)
network_data = self._deserialize_net_response(content_type,
show_network_res)
- self.assertEqual({'id': network_id,
- 'name': self.network_name},
- network_data['network'])
+ self.assert_network(id=network_id, name=self.network_name,
+ network_data=network_data['network'])
LOG.debug("_test_show_network - fmt:%s - END", fmt)
def _test_show_network_detail(self, fmt):
@@ -174,11 +173,9 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_network_res.status_int, 200)
network_data = self._deserialize_net_response(content_type,
show_network_res)
- self.assertEqual({'id': network_id,
- 'name': self.network_name,
- 'ports': [{'id': port_id,
- 'state': 'ACTIVE'}]},
- network_data['network'])
+ self.assert_network_details(id=network_id, name=self.network_name,
+ port_id=port_id, port_state='ACTIVE',
+ network_data=network_data['network'])
LOG.debug("_test_show_network_detail - fmt:%s - END", fmt)
def _test_show_network_not_found(self, fmt):
@@ -208,9 +205,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_network_res.status_int, 200)
network_data = self._deserialize_net_response(content_type,
show_network_res)
- self.assertEqual({'id': network_id,
- 'name': new_name},
- network_data['network'])
+ self.assert_network(id=network_id, name=new_name,
+ network_data=network_data['network'])
LOG.debug("_test_rename_network - fmt:%s - END", fmt)
def _test_rename_network_badrequest(self, fmt):
@@ -365,8 +361,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_port_res.status_int, 200)
port_data = self._deserialize_port_response(content_type,
show_port_res)
- self.assertEqual({'id': port_id, 'state': port_state},
- port_data['port'])
+ self.assert_port(id=port_id, state=port_state,
+ port_data=port_data['port'])
LOG.debug("_test_show_port - fmt:%s - END", fmt)
def _test_show_port_detail(self, fmt):
@@ -383,8 +379,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_port_res.status_int, 200)
port_data = self._deserialize_port_response(content_type,
show_port_res)
- self.assertEqual({'id': port_id, 'state': port_state},
- port_data['port'])
+ self.assert_port(id=port_id, state=port_state,
+ port_data=port_data['port'])
# Part 2 - plug attachment into port
interface_id = "test_interface"
@@ -401,9 +397,9 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_port_res.status_int, 200)
port_data = self._deserialize_port_response(content_type,
show_port_res)
- self.assertEqual({'id': port_id, 'state': port_state,
- 'attachment': {'id': interface_id}},
- port_data['port'])
+ self.assert_port_attachment(id=port_id, state=port_state,
+ interface_id=interface_id,
+ port_data=port_data['port'])
LOG.debug("_test_show_port_detail - fmt:%s - END", fmt)
@@ -575,8 +571,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_port_res.status_int, 200)
port_data = self._deserialize_port_response(content_type,
show_port_res)
- self.assertEqual({'id': port_id, 'state': new_port_state},
- port_data['port'])
+ self.assert_port(id=port_id, state=new_port_state,
+ port_data=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,
@@ -591,8 +587,8 @@ class AbstractAPITest(unittest.TestCase):
self.assertEqual(show_port_res.status_int, 200)
port_data = self._deserialize_port_response(content_type,
show_port_res)
- self.assertEqual({'id': port_id, 'state': port_state},
- port_data['port'])
+ self.assert_port(id=port_id, state=port_state,
+ port_data=port_data['port'])
LOG.debug("_test_set_port_state - fmt:%s - END", fmt)
def _test_set_port_state_networknotfound(self, fmt):
diff --git a/quantum/tests/unit/test_api.py b/quantum/tests/unit/test_api.py
index 5d637c9e54..57c5b4cb44 100644
--- a/quantum/tests/unit/test_api.py
+++ b/quantum/tests/unit/test_api.py
@@ -21,9 +21,33 @@ import quantum.api.networks as nets
import quantum.api.ports as ports
import quantum.tests.unit._test_api as test_api
+from quantum.common.test_lib import test_config
+
class APITestV10(test_api.AbstractAPITest):
+ def assert_network(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'name': kwargs['name']},
+ kwargs['network_data'])
+
+ def assert_network_details(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'name': kwargs['name'],
+ 'ports': [{'id': kwargs['port_id'],
+ 'state': 'ACTIVE'}]},
+ kwargs['network_data'])
+
+ def assert_port(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'state': kwargs['state']},
+ kwargs['port_data'])
+
+ def assert_port_attachment(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'],
+ 'attachment': {'id': kwargs['interface_id']}},
+ kwargs['port_data'])
+
def setUp(self):
super(APITestV10, self).setUp('quantum.api.APIRouterV10',
{test_api.NETS: nets.ControllerV10._serialization_metadata,
@@ -33,7 +57,38 @@ class APITestV10(test_api.AbstractAPITest):
class APITestV11(test_api.AbstractAPITest):
+ def assert_network(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'name': kwargs['name'],
+ 'op-status': self.net_op_status},
+ kwargs['network_data'])
+
+ def assert_network_details(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'name': kwargs['name'],
+ 'op-status': self.net_op_status,
+ 'ports': [{'id': kwargs['port_id'],
+ 'state': 'ACTIVE',
+ 'op-status': self.port_op_status}]},
+ kwargs['network_data'])
+
+ def assert_port(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'],
+ 'state': kwargs['state'],
+ 'op-status': self.port_op_status},
+ kwargs['port_data'])
+
+ def assert_port_attachment(self, **kwargs):
+ self.assertEqual({'id': kwargs['id'], 'state': kwargs['state'],
+ 'op-status': self.port_op_status,
+ 'attachment': {'id': kwargs['interface_id']}},
+ kwargs['port_data'])
+
def setUp(self):
+ self.net_op_status = test_config.get('default_net_op_status',
+ 'UNKNOWN')
+ self.port_op_status = test_config.get('default_port_op_status',
+ 'UNKNOWN')
super(APITestV11, self).setUp('quantum.api.APIRouterV11',
{test_api.NETS: nets.ControllerV11._serialization_metadata,
test_api.PORTS: ports.ControllerV11._serialization_metadata,