add extension code in.(last push does not include this directory.)

This commit is contained in:
Ying Liu 2011-07-15 17:40:33 -07:00
parent a8915ac3bc
commit 6d25812ace
6 changed files with 597 additions and 0 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

111
cisco_extensions/faults.py Normal file
View File

@ -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.')

View File

@ -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))

View File

@ -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']))