]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
add extension code in.(last push does not include this directory.)
authorYing Liu <yinliu2@cisco.com>
Sat, 16 Jul 2011 00:40:33 +0000 (17:40 -0700)
committerYing Liu <yinliu2@cisco.com>
Sat, 16 Jul 2011 00:40:33 +0000 (17:40 -0700)
cisco_extensions/__init__.py [new file with mode: 0644]
cisco_extensions/exceptions.py [new file with mode: 0644]
cisco_extensions/extensions.py [new file with mode: 0644]
cisco_extensions/faults.py [new file with mode: 0644]
cisco_extensions/portprofiles.py [new file with mode: 0644]
cisco_extensions/pprofiles.py [new file with mode: 0644]

diff --git a/cisco_extensions/__init__.py b/cisco_extensions/__init__.py
new file mode 100644 (file)
index 0000000..5fc5d88
--- /dev/null
@@ -0,0 +1,71 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+
+import logging
+import routes
+import webob.dec
+import webob.exc
+
+from quantum import manager
+from quantum.api import faults
+from quantum.api import networks
+from quantum.api import ports
+from quantum.common import flags
+from quantum.common import wsgi
+from cisco_extensions import portprofiles
+from cisco_extensions import extensions
+
+
+LOG = logging.getLogger('quantum_extension.api')
+FLAGS = flags.FLAGS
+
+
+class ExtRouterV01(wsgi.Router):
+    """
+    Routes requests on the Quantum API to the appropriate controller
+    """
+    
+    def __init__(self, ext_mgr=None):
+        uri_prefix = '/tenants/{tenant_id}/'
+       
+        mapper = routes.Mapper()
+        plugin = manager.QuantumManager().get_plugin() 
+        controller = portprofiles.Controller(plugin)
+        ext_controller = extensions.Controller(plugin)
+        mapper.connect("home", "/", controller=ext_controller, 
+                       action="list_extension", 
+                       conditions=dict(method=['GET']))
+        #mapper.redirect("/", "www.google.com")
+        mapper.resource("portprofiles", "portprofiles",
+                        controller=controller,
+                        path_prefix=uri_prefix)
+        mapper.connect("associate_portprofile",
+                       uri_prefix 
+                       + 'portprofiles/{portprofile_id}/assignment{.format}',
+                       controller=controller,
+                       action="associate_portprofile",
+                       conditions=dict(method=['PUT']))
+        mapper.connect("disassociate_portprofile",
+                       uri_prefix 
+                       + 'portprofiles/{portprofile_id}/assignment{.format}',
+                       controller=controller,
+                       action="disassociate_portprofile",
+                       conditions=dict(method=['DELETE']))
+      
+        super(ExtRouterV01, self).__init__(mapper)
diff --git a/cisco_extensions/exceptions.py b/cisco_extensions/exceptions.py
new file mode 100644 (file)
index 0000000..415731e
--- /dev/null
@@ -0,0 +1,148 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+import logging
+
+
+class ExtensionException(Exception):
+    """Quantum Cisco api Exception
+    
+    Taken from nova.exception.NovaException
+    To correctly use this class, inherit from it and define
+    a 'message' property. That message will get printf'd
+    with the keyword arguments provided to the constructor.
+
+    """
+    message = _("An unknown exception occurred.")
+
+    def __init__(self, **kwargs):
+        try:
+            self._error_string = self.message % kwargs
+
+        except Exception:
+            # at least get the core message out if something happened
+            self._error_string = self.message
+
+    def __str__(self):
+        return self._error_string
+
+
+class ProcessExecutionError(IOError):
+    def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None,
+                 description=None):
+        if description is None:
+            description = "Unexpected error while running command."
+        if exit_code is None:
+            exit_code = '-'
+        message = "%s\nCommand: %s\nExit code: %s\nStdout: %r\nStderr: %r" % (
+                  description, cmd, exit_code, stdout, stderr)
+        IOError.__init__(self, message)
+
+
+class Error(Exception):
+    def __init__(self, message=None):
+        super(Error, self).__init__(message)
+
+
+class ApiError(Error):
+    def __init__(self, message='Unknown', code='Unknown'):
+        self.message = message
+        self.code = code
+        super(ApiError, self).__init__('%s: %s' % (code, message))
+
+
+class NotFound(ExtensionException):
+    pass
+
+
+class ClassNotFound(NotFound):
+    message = _("Class %(class_name)s could not be found")
+
+
+class PortprofileNotFound(NotFound):
+    message = _("Portprofile %(_id)s could not be found")
+
+
+class PortNotFound(NotFound):
+    message = _("Port %(port_id)s could not be found " \
+                "on Network %(net_id)s")
+    
+
+"""
+    
+
+class PortprofileInUse(ExtensionException):
+    message = _("Unable to complete operation on Portprofile %(net_id)s. " \
+                "There is one or more attachments plugged into its ports.")
+
+
+class PortInUse(ExtensionException):
+    message = _("Unable to complete operation on port %(port_id)s " \
+                "for Portprofile %(net_id)s. The attachment '%(att_id)s" \
+                "is plugged into the logical port.")
+
+class AlreadyAttached(ExtensionException):
+    message = _("Unable to plug the attachment %(att_id)s into port " \
+                "%(port_id)s for Portprofile %(net_id)s. The attachment is " \
+                "already plugged into port %(att_port_id)s")
+    
+"""
+
+
+class Duplicate(Error):
+    pass
+
+
+class NotAuthorized(Error):
+    pass
+
+
+class NotEmpty(Error):
+    pass
+
+
+class Invalid(Error):
+    pass
+
+
+class InvalidContentType(Invalid):
+    message = _("Invalid content type %(content_type)s.")
+
+
+class BadInputError(Exception):
+    """Error resulting from a client sending bad input to a server"""
+    pass
+
+
+class MissingArgumentError(Error):
+    pass
+
+
+def wrap_exception(f):
+    def _wrap(*args, **kw):
+        try:
+            return f(*args, **kw)
+        except Exception, e:
+            if not isinstance(e, Error):
+                #exc_type, exc_value, exc_traceback = sys.exc_info()
+                logging.exception('Uncaught exception')
+                #logging.error(traceback.extract_stack(exc_traceback))
+                raise Error(str(e))
+            raise
+    _wrap.func_name = f.func_name
+    return _wrap
diff --git a/cisco_extensions/extensions.py b/cisco_extensions/extensions.py
new file mode 100644 (file)
index 0000000..34bc37e
--- /dev/null
@@ -0,0 +1,42 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+import logging
+import webob.dec
+
+from quantum.common import wsgi
+from quantum.api import api_common as common
+
+
+LOG = logging.getLogger('quantum.api.cisco_extension.extensions')
+
+
+class Controller(common.QuantumController):
+
+    def __init__(self, plugin):
+        #self._plugin = plugin
+        #super(QuantumController, self).__init__()
+        self._resource_name = 'extensions'
+        super(Controller, self).__init__(plugin)
+        
+    def list_extension(self, req):
+        """Respond to a request for listing all extension api."""
+        response = "extensions api list"
+        return response
+    
+        
\ No newline at end of file
diff --git a/cisco_extensions/faults.py b/cisco_extensions/faults.py
new file mode 100644 (file)
index 0000000..c965f73
--- /dev/null
@@ -0,0 +1,111 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+import webob.dec
+import webob.exc
+
+from quantum.api import api_common as common
+from quantum.common import wsgi
+
+
+class Fault(webob.exc.HTTPException):
+    """Error codes for API faults"""
+
+    _fault_names = {
+            400: "malformedRequest",
+            401: "unauthorized",
+            420: "networkNotFound",
+            421: "PortprofileInUse",
+            430: "portNotFound",
+            431: "requestedStateInvalid",
+            432: "portInUse",
+            440: "alreadyAttached",
+            450: "PortprofileNotFound",
+            470: "serviceUnavailable",
+            471: "pluginFault"}
+
+    def __init__(self, exception):
+        """Create a Fault for the given webob.exc.exception."""
+        self.wrapped_exc = exception
+
+    @webob.dec.wsgify(RequestClass=wsgi.Request)
+    def __call__(self, req):
+        """Generate a WSGI response based on the exception passed to ctor."""
+        #print ("*********TEST2")
+        # Replace the body with fault details.
+        code = self.wrapped_exc.status_int
+        fault_name = self._fault_names.get(code, "quantumServiceFault")
+        fault_data = {
+            fault_name: {
+                'code': code,
+                'message': self.wrapped_exc.explanation, 
+                'detail': self.wrapped_exc.detail}}
+        # 'code' is an attribute on the fault tag itself
+        metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
+        default_xmlns = common.XML_NS_V10
+        serializer = wsgi.Serializer(metadata, default_xmlns)
+        content_type = req.best_match_content_type()
+        self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
+        self.wrapped_exc.content_type = content_type
+        return self.wrapped_exc
+
+
+class PortprofileNotFound(webob.exc.HTTPClientError):
+    """
+    subclass of :class:`~HTTPClientError`
+
+    This indicates that the server did not find the Portprofile specified
+    in the HTTP request
+
+    code: 450, title: Portprofile not Found
+    """
+    #print ("*********TEST1")
+    code = 450
+    title = 'Portprofile Not Found'
+    explanation = ('Unable to find a Portprofile with' 
+                   + ' the specified identifier.')
+
+
+class PortNotFound(webob.exc.HTTPClientError):
+    """
+    subclass of :class:`~HTTPClientError`
+
+    This indicates that the server did not find the port specified
+    in the HTTP request for a given network
+
+    code: 430, title: Port not Found
+    """
+    code = 430
+    title = 'Port not Found'
+    explanation = ('Unable to find a port with the specified identifier.')
+
+
+class RequestedStateInvalid(webob.exc.HTTPClientError):
+    """
+    subclass of :class:`~HTTPClientError`
+
+    This indicates that the server could not update the port state to
+    to the request value
+
+    code: 431, title: Requested State Invalid
+    """
+    code = 431
+    title = 'Requested State Invalid'
+    explanation = ('Unable to update port state with specified value.')
+
+
diff --git a/cisco_extensions/portprofiles.py b/cisco_extensions/portprofiles.py
new file mode 100644 (file)
index 0000000..5d19552
--- /dev/null
@@ -0,0 +1,180 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2011 Cisco Systems, Inc.  All rights reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# @author: Ying Liu, Cisco Systems, Inc.
+#
+
+import logging
+import webob.dec
+from quantum.common import wsgi
+from webob import exc
+
+from quantum.api import api_common as common
+
+from cisco_extensions import pprofiles as pprofiles_view
+from cisco_extensions import exceptions as exception
+from cisco_extensions import faults as faults
+
+LOG = logging.getLogger('quantum.api.portprofiles')
+
+
+class Controller(common.QuantumController):
+    """ portprofile API controller
+        based on QuantumController """
+
+    _portprofile_ops_param_list = [{
+        'param-name': 'portprofile-name',
+        'required': True}, {
+        'param-name': 'vlan-id',
+        'required': True}, {
+        'param-name': 'assignment',
+        'required': False}]
+    
+    _assignprofile_ops_param_list = [{
+        'param-name': 'network-id',
+        'required': True}, {
+        'param-name': 'port-id',
+        'required': True}]
+    
+    _serialization_metadata = {
+        "application/xml": {
+            "attributes": {
+                "portprofile": ["id", "name"],
+            },
+        },
+    }
+
+    def __init__(self, plugin):
+        self._resource_name = 'portprofile'
+        super(Controller, self).__init__(plugin)
+             
+    def index(self, request, tenant_id):
+        """ Returns a list of portprofile ids """
+        #TODO: this should be for a given tenant!!!
+        return self._items(request, tenant_id, is_detail=False)
+
+    def _items(self, request, tenant_id, is_detail):
+        """ Returns a list of portprofiles. """
+        portprofiles = self._plugin.get_all_portprofiles(tenant_id)
+        builder = pprofiles_view.get_view_builder(request)
+        result = [builder.build(portprofile, is_detail)['portprofile']
+                  for portprofile in portprofiles]
+        return dict(portprofiles=result)
+
+    def show(self, request, tenant_id, id):
+        """ Returns portprofile details for the given portprofile id """
+        try:
+            portprofile = self._plugin.get_portprofile_details(
+                            tenant_id, id)
+            builder = pprofiles_view.get_view_builder(request)
+            #build response with details
+            result = builder.build(portprofile, True)
+            return dict(portprofiles=result)
+        except exception.PortprofileNotFound as e:
+            return faults.Fault(faults.PortprofileNotFound(e))
+            #return faults.Fault(e)
+
+    def create(self, request, tenant_id):
+        """ Creates a new portprofile for a given tenant """
+        #look for portprofile name in request
+        try:
+            req_params = \
+                self._parse_request_params(request, 
+                                           self._portprofile_ops_param_list)
+        except exc.HTTPError as e:
+            return faults.Fault(e)
+        portprofile = self._plugin.\
+                       create_portprofile(tenant_id,
+                                          req_params['portprofile-name'],
+                                          req_params['vlan-id'])
+        builder = pprofiles_view.get_view_builder(request)
+        result = builder.build(portprofile)
+        return dict(portprofiles=result)
+
+    def update(self, request, tenant_id, id):
+        """ Updates the name for the portprofile with the given id """
+        try:
+            req_params = \
+                self._parse_request_params(request, 
+                                           self._portprofile_ops_param_list)
+        except exc.HTTPError as e:
+            return faults.Fault(e)
+        try:
+            portprofile = self._plugin.\
+            rename_portprofile(tenant_id,
+                        id, req_params['portprofile-name'])
+
+            builder = pprofiles_view.get_view_builder(request)
+            result = builder.build(portprofile, True)
+            return dict(portprofiles=result)
+        except exception.PortprofileNotFound as e:
+            return faults.Fault(faults.PortprofileNotFound(e))
+
+    def delete(self, request, tenant_id, id):
+        """ Destroys the portprofile with the given id """
+        try:
+            self._plugin.delete_portprofile(tenant_id, id)
+            return exc.HTTPAccepted()
+        except exception.PortprofileNotFound as e:
+            return faults.Fault(faults.PortprofileNotFound(e))
+         
+    #added for cisco's extension
+    def associate_portprofile(self, request, tenant_id, portprofile_id):
+        content_type = request.best_match_content_type()
+        print "Content type:%s" % content_type
+        
+        try:
+            req_params = \
+                self._parse_request_params(request,
+                                           self._assignprofile_ops_param_list)
+        except exc.HTTPError as e:
+            return faults.Fault(e)
+        net_id = req_params['network-id'].strip()
+        #print "*****net id "+net_id
+        port_id = req_params['port-id'].strip()
+        try:
+            self._plugin.associate_portprofile(tenant_id,
+                                                net_id, port_id,
+                                                portprofile_id)
+            return exc.HTTPAccepted()
+        except exception.PortprofileNotFound as e:
+            return faults.Fault(faults.PortprofileNotFound(e))
+        except exception.PortNotFound as e:
+            return faults.Fault(faults.PortNotFound(e))
+        
+     #added for Cisco extension
+    def disassociate_portprofile(self, request, tenant_id, portprofile_id):
+        content_type = request.best_match_content_type()
+        print "Content type:%s" % content_type
+        
+        try:
+            req_params = \
+                self._parse_request_params(request,
+                                           self._assignprofile_ops_param_list)
+        except exc.HTTPError as e:
+            return faults.Fault(e)
+        net_id = req_params['network-id'].strip()
+        #print "*****net id "+net_id
+        port_id = req_params['port-id'].strip()
+        try:
+            self._plugin. \
+            disassociate_portprofile(tenant_id,
+                                    net_id, port_id, portprofile_id)
+            return exc.HTTPAccepted()
+        except exception.PortprofileNotFound as e:
+            return faults.Fault(faults.PortprofileNotFound(e))
+        except exception.PortNotFound as e:
+            return faults.Fault(faults.PortNotFound(e))
diff --git a/cisco_extensions/pprofiles.py b/cisco_extensions/pprofiles.py
new file mode 100644 (file)
index 0000000..ba7f8a3
--- /dev/null
@@ -0,0 +1,45 @@
+
+
+import os
+
+
+def get_view_builder(req):
+    base_url = req.application_url
+    return ViewBuilder(base_url)
+
+
+class ViewBuilder(object):
+    """
+    ViewBuilder for Portprofile, 
+    derived from quantum.views.networks
+    """
+    def __init__(self, base_url):
+        """
+        :param base_url: url of the root wsgi application
+        """
+        self.base_url = base_url
+
+    def build(self, portprofile_data, is_detail=False):
+        """Generic method used to generate a portprofile entity."""
+        print "portprofile-DATA:%s" %portprofile_data
+        if is_detail:
+            portprofile = self._build_detail(portprofile_data)
+        else:
+            portprofile = self._build_simple(portprofile_data)
+        return portprofile
+    
+    def _build_simple(self, portprofile_data):
+        """Return a simple model of a server."""
+        return dict(portprofile=dict(id=portprofile_data['profile-id']))
+    
+    def _build_detail(self, portprofile_data):
+        """Return a simple model of a server."""
+        if (portprofile_data['assignment']==None):
+            return dict(portprofile=dict(id=portprofile_data['profile-id'],
+                                name=portprofile_data['profile-name'],
+                                vlan_id=portprofile_data['vlan-id']))
+        else:
+            return dict(portprofile=dict(id=portprofile_data['profile-id'],
+                                name=portprofile_data['profile-name'],
+                                vlan_id=portprofile_data['vlan-id'],
+                                assignment=portprofile_data['assignment']))