From: Ying Liu Date: Wed, 17 Aug 2011 19:26:32 +0000 (-0700) Subject: add extension change to ying's branch X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=74837113e60007fcca498c2479039ac592557e65;p=openstack-build%2Fneutron-build.git add extension change to ying's branch --- diff --git a/cisco_demo/demo_client.py b/cisco_demo/demo_client.py new file mode 100755 index 000000000..74b4a6a7a --- /dev/null +++ b/cisco_demo/demo_client.py @@ -0,0 +1,385 @@ + +import os +import sys + +import gettext + +#gettext.install('quantum', unicode=1) +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')): + sys.path.insert(0, possible_topdir) + +gettext.install('quantum', unicode=1) + +from test_scripts.miniclient import MiniClient +from test_client import ExtClient +from quantum.common.wsgi import Serializer + +HOST = '127.0.0.1' +PORT = 9696 +USE_SSL = False +TENANT_ID = 'ucs_user' + + +test_network_data = \ + {'network': {'net-name': 'cisco_test_network', + 'valn-id': 28}} +test_portprofile_data = \ + {'portprofile': {'portprofile_name': 'cisco_test_portprofile', + 'qos_name': 2, + 'qos_name': 'test-qos'}} +test_cred_data = \ + {'credential': {'credential_name': 'cred3', + 'user_name': 'newUser', + 'password': 'newPasswd' + }} +test_qos_data = \ + {'qos': {'qos_name': 'plantimum', + 'qos_desc': {'PPS': 50, 'TTL': 5}}} + + +#we put this assignment under portprofile resources +#therefore we need to create such a test data +test_port_assign_data = {'portprofile': {'network-id': '001', + 'port-id': '1'}} +test_attach_data = {'port': {'attachment-id': 'v01'}} + +test_act_data = {"get_available_host":123} + +test_instance_data={'novatenant':{'instance_id' : 1, + 'instance_desc' : {'key1' : '1', + 'key2' : '2' + }}} + +def test_get_host(format='json'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + body = Serializer().serialize(test_instance_data, content_type) + res = client.do_request(TENANT_ID, + 'PUT', "/novatenants/001/get_host." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_get_instance_port(format='json'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + body = Serializer().serialize(test_instance_data, content_type) + res = client.do_request(TENANT_ID, + 'PUT', "/novatenants/001/get_instance_port." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_action_ext(format='json'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + action_name = 'get_available_host' + action_params = dict(name='test') + body = Serializer().serialize(test_act_data, content_type) + + + res = client.do_request(TENANT_ID, 'POST', "/act_resources/1/action." + format, body=body) + content = print_response(res) + +def print_response(res): + content = res.read() + print "Status: %s" % res.status + print "Content: %s" % content + return content + + +def create_cisco_network(format='xml'): + + client = MiniClient(HOST, PORT, USE_SSL) + print "CREATE NETWORK -- FORMAT:%s" % format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_network_data, content_type) + res = client.do_request(TENANT_ID, + 'POST', "/networks." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def create_cisco_portprofile(format='xml'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "List all Profile -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/portprofiles." + format) + content = print_response(res) + portprofile_data = Serializer().deserialize(content, content_type) + print portprofile_data + + print "List a specific Profile -- FORMAT:%s" % format + profile_id = portprofile_data['portprofiles'][0]['id'] + #profile_id='001' + print "profile_id " + profile_id + res = client.do_request(TENANT_ID, + 'GET', "/portprofiles/" + + profile_id + "." + format) + print_response(res) + + print "CREATE Profile -- FORMAT:%s" % format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_portprofile_data, content_type) + print "***BODY is " + print body + res = client.do_request(TENANT_ID, 'POST', + "/portprofiles." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_credential (format='xml'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "----------------------------" + print "List all credentials -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/credentials." + format) + content = print_response(res) + credential_data = Serializer().deserialize(content, content_type) + print credential_data + + print "----------------------------" + print "CREATE Credential -- FORMAT:%s" % format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_cred_data, content_type) + print "***BODY is " + print body + res = client.do_request(TENANT_ID, 'POST', + "/credentials." + format, body=body) + print "XML Response" + print_response(res) + + print "----------------------------" + print "List all credentials -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/credentials." + format) + content = print_response(res) + credential_data = Serializer().deserialize(content, content_type) + print credential_data + print "----------------------------" + print "List a specific cred -- FORMAT:%s" % format + print "----------------------------" + cred_id = credential_data['credentials'][0]['id'] + #cred_id='001' + print "cred_id " + cred_id + res = client.do_request(TENANT_ID, + 'GET', "/credentials/" + + cred_id + "." + format) + print_response(res) + + print "----------------------------" + print "TEST DELETE Credential -- FORMAT:%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'DELETE', + "/credentials/" + cred_id + "." + format) + print_response(res) + + print "----------------------------" + print "List all credentials -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/credentials." + format) + content = print_response(res) + credential_data = Serializer().deserialize(content, content_type) + print credential_data + + print "COMPLETED" + print "----------------------------" + +def test_qos (format='xml'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "----------------------------" + print "List all qoss -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/qoss." + format) + content = print_response(res) + qos_data = Serializer().deserialize(content, content_type) + print qos_data + + print "----------------------------" + print "CREATE qos -- FORMAT:%s" % format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_qos_data, content_type) + print "***BODY is " + print body + res = client.do_request(TENANT_ID, 'POST', + "/qoss." + format, body=body) + print "XML Response" + print_response(res) + + print "----------------------------" + print "List all qoss -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/qoss." + format) + content = print_response(res) + qos_data = Serializer().deserialize(content, content_type) + print qos_data + print "----------------------------" + print "List a specific cred -- FORMAT:%s" % format + print "----------------------------" + qos_id = qos_data['qoss'][0]['id'] + #cred_id='001' + print "qos_id " + qos_id + res = client.do_request(TENANT_ID, + 'GET', "/qoss/" + + qos_id + "." + format) + print_response(res) + + print "----------------------------" + print "TEST DELETE qos -- FORMAT:%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'DELETE', + "/qoss/" + qos_id + "." + format) + print_response(res) + + print "----------------------------" + print "List all qoss -- FORMat%s" % format + print "----------------------------" + res = client.do_request(TENANT_ID, 'GET', "/qoss." + format) + content = print_response(res) + qos_data = Serializer().deserialize(content, content_type) + print qos_data + + print "COMPLETED" + print "----------------------------" + +def test_delete_network(format='xml'): + client = MiniClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST DELETE NETWORK -- FORMAT:%s" % format + print "----------------------------" + print "--> Step 1 - List All Networks" + res = client.do_request(TENANT_ID, 'GET', "/networks." + format) + content = print_response(res) + network_data = Serializer().deserialize(content, content_type) + print network_data + net_id = network_data['networks'][0]['id'] + print "--> Step 2 - Delete network %s" % net_id + res = client.do_request(TENANT_ID, 'DELETE', + "/networks/" + net_id + "." + format) + print_response(res) + print "--> Step 3 - List All Networks (Again)" + res = client.do_request(TENANT_ID, 'GET', "/networks." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def test_delete_portprofile(format='xml'): + client = ExtClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST DELETE PROFILE -- FORMAT:%s" % format + print "----------------------------" + print "--> Step 1 - List All Profiles" + res = client.do_request(TENANT_ID, 'GET', "/portprofiles." + format) + content = print_response(res) + portprofile_data = Serializer().deserialize(content, content_type) + print portprofile_data + profile_id = portprofile_data['portprofiles'][0]['id'] + print "--> Step 2 - Delete portprofile %s" % profile_id + res = client.do_request(TENANT_ID, 'DELETE', + "/portprofiles/" + profile_id + "." + format) + print_response(res) + print "--> Step 3 - List All Profiles (Again)" + res = client.do_request(TENANT_ID, 'GET', "/portprofiles." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def test_create_port(format='xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "TEST CREATE PORT -- FORMAT:%s" % format + print "----------------------------" + print "--> Step 1 - List Ports for network 001" + res = client.do_request(TENANT_ID, 'GET', "/networks/001/ports." + format) + print_response(res) + print "--> Step 2 - Create Port for network 001" + res = client.do_request(TENANT_ID, 'POST', "/networks/001/ports." + format) + print_response(res) + print "--> Step 3 - List Ports for network 001 (again)" + res = client.do_request(TENANT_ID, 'GET', "/networks/001/ports." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +#assuming network 001 and ports 1 are created in the plug-in +def test_attach_resource(format='xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "TEST attach resources to port" + content_type = "application/" + format + body = Serializer().serialize(test_attach_data, content_type) + #attach virtual interface to the port + res = client.do_request(TENANT_ID, 'PUT', + "/networks/001/ports/1/attachment." + + format, body=body) + print_response(res) + #list existing interface of the port + res = client.do_request(TENANT_ID, 'GET', + "/networks/001/ports/1/attachment." + format) + print_response(res) + #de_attach virtual interface from the port + res = client.do_request(TENANT_ID, 'DELETE', + "/networks/001/ports/1/attachment." + format) + print_response(res) + #list existing interface of the port + res = client.do_request(TENANT_ID, 'GET', + "/networks/001/ports/1/attachment." + format) + print_response(res) + + +#assuming network 001, ports 1 and portprofile 002 are created in the plug-in +def test_assign_portprofile(format='xml'): + client = ExtClient(HOST, PORT, USE_SSL) + print "TEST attach resources to port" + content_type = "application/" + format + body = Serializer().serialize(test_port_assign_data, content_type) + print "body is " + body + res = client.do_request(TENANT_ID, 'PUT', + "/portprofiles/001/associate_portprofile." + + format, body=body) + print_response(res) + res = client.do_request(TENANT_ID, 'POST', + "/portprofiles/001/disassociate_portprofile." + + format, body=body) + + print_response(res) + + +def main(): + create_cisco_portprofile('json') + test_attach_resource('json') + + test_delete_portprofile('json') + test_credential('json') + test_qos('json') + ##test_action_ext('json') + test_get_host('json') + test_get_instance_port('json') + + #create_cisco_network('json') + #test_create_port('json') + #create_cisco_portprofile('json') + #test_assign_portprofile('json') + pass + + +# Standard boilerplate to call the main() function. +if __name__ == '__main__': + main() diff --git a/cisco_demo/demo_client.py.bk b/cisco_demo/demo_client.py.bk new file mode 100755 index 000000000..3c7c928d4 --- /dev/null +++ b/cisco_demo/demo_client.py.bk @@ -0,0 +1,127 @@ +''' +Created on Jun 08, 2011 + +@author: ying +''' + +import os +import sys + +import gettext + +#gettext.install('quantum', unicode=1) +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')): + sys.path.insert(0, possible_topdir) + +gettext.install('quantum', unicode=1) + +from test_scripts.miniclient import MiniClient +from quantum.common.wsgi import Serializer + +HOST = '127.0.0.1' +PORT = 9696 +USE_SSL = False +TENANT_ID = 'ucs_user' + +test_network_data = \ + {'network': {'network-name': 'cisco_test_network', + 'valn-id': 28 }} + +test_portprofile_data = \ + {'portprofile': {'portprofile-name': 'cisco_test_portprofile', + 'vlan-id': 28, + 'vlan-name': 'test-vlan' }} + +def print_response(res): + content = res.read() + print "Status: %s" %res.status + print "Content: %s" %content + return content + + +def create_cisco_network(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "CREATE NETWORK -- FORMAT:%s" %format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_network_data, content_type) + res = client.do_request(TENANT_ID,'POST', "/networks." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + +def create_cisco_portprofile(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "CREATE Profile -- FORMAT:%s" %format + print "----------------------------" + content_type = "application/" + format + body = Serializer().serialize(test_portprofile_data, content_type) + print "**********BODY is**********" + print body + print "***************************" + res = client.do_request(TENANT_ID,'POST', "/portprofiles." + format, body=body) + print "XML Response" + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_delete_network(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST DELETE NETWORK -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - List All Networks" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + content = print_response(res) + network_data = Serializer().deserialize(content, content_type) + print network_data + net_id = network_data['networks'][0]['id'] + print "--> Step 2 - Delete network %s" %net_id + res = client.do_request(TENANT_ID,'DELETE', + "/networks/" + net_id + "." + format) + print_response(res) + print "--> Step 3 - List All Networks (Again)" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def test_delete_portprofile(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST DELETE PROFILE -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - List All Profiles" + res = client.do_request(TENANT_ID,'GET', "/portprofiles." + format) + content = print_response(res) + portprofile_data = Serializer().deserialize(content, content_type) + print portprofile_data + profile_id = portprofile_data['portprofiles'][0]['id'] + print "--> Step 2 - Delete portprofile %s" %profile_id + res = client.do_request(TENANT_ID,'DELETE', + "/portprofiles/" + profile_id + "." + format) + print_response(res) + print "--> Step 3 - List All Profiles (Again)" + res = client.do_request(TENANT_ID,'GET', "/portprofiles." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + +def main(): + + create_cisco_network('xml') + + create_cisco_portprofile('xml') + #test_delete_network('json') + test_delete_portprofile('json') + pass + + +# Standard boilerplate to call the main() function. +if __name__ == '__main__': + main() diff --git a/cisco_demo/test_client.py b/cisco_demo/test_client.py new file mode 100644 index 000000000..4273e7d4a --- /dev/null +++ b/cisco_demo/test_client.py @@ -0,0 +1,83 @@ +"""A base client class - derived from Quantum.MiniClient""" + +import httplib +import socket +import urllib + + +class ExtClient(object): + + action_prefix = '/v0.1/extensions/csco/tenants/{tenant_id}' + #action_prefix = '/v0.1/tenants/{tenant_id}' + def __init__(self, host, port, use_ssl): + """ + Creates a new client to some service. + + :param host: The host where service resides + :param port: The port where service resides + :param use_ssl: Should we use HTTPS? + """ + self.host = host + self.port = port + self.use_ssl = use_ssl + self.connection = None + + def get_connection_type(self): + """ + Returns the proper connection type + """ + if self.use_ssl: + return httplib.HTTPSConnection + else: + return httplib.HTTPConnection + + def do_request(self, tenant, method, action, body=None, + headers=None, params=None): + """ + Connects to the server and issues a request. + Returns the result data, or raises an appropriate exception if + HTTP status code is not 2xx + + :param method: HTTP method ("GET", "POST", "PUT", etc...) + :param body: string of data to send, or None (default) + :param headers: mapping of key/value pairs to add as headers + :param params: dictionary of key/value pairs to add to append + to action + + """ + action = ExtClient.action_prefix + action + action = action.replace('{tenant_id}', tenant) + if type(params) is dict: + action += '?' + urllib.urlencode(params) + + try: + connection_type = self.get_connection_type() + headers = headers or {} + + # Open connection and send request + c = connection_type(self.host, self.port) + c.request(method, action, body, headers) + res = c.getresponse() + status_code = self.get_status_code(res) + if status_code in (httplib.OK, + httplib.CREATED, + httplib.ACCEPTED, + httplib.NO_CONTENT): + return res + else: + raise Exception("Server returned error: %s" % res.read()) + + except (socket.error, IOError), e: + raise Exception("Unable to connect to " + "server. Got error: %s" % e) + + def get_status_code(self, response): + """ + Returns the integer status code from the response, which + can be either a Webob.Response (used in testing) or httplib.Response + """ + if hasattr(response, 'status_int'): + return response.status_int + else: + return response.status + diff --git a/extensions/_credential_view.py b/extensions/_credential_view.py new file mode 100644 index 000000000..e324d3fc3 --- /dev/null +++ b/extensions/_credential_view.py @@ -0,0 +1,35 @@ +def get_view_builder(req): + base_url = req.application_url + return ViewBuilder(base_url) + + +class ViewBuilder(object): + """ + ViewBuilder for Credential, + 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, credential_data, is_detail=False): + """Generic method used to generate a credential entity.""" + print "credential-DATA:%s" % credential_data + if is_detail: + credential = self._build_detail(credential_data) + else: + credential = self._build_simple(credential_data) + return credential + + def _build_simple(self, credential_data): + """Return a simple model of a server.""" + return dict(credential=dict(id=credential_data['credential_id'])) + + def _build_detail(self, credential_data): + """Return a simple model of a server.""" + + return dict(credential=dict(id=credential_data['credential_id'], + name=credential_data['user_name'], + password=credential_data['password'])) \ No newline at end of file diff --git a/extensions/_exceptions.py b/extensions/_exceptions.py new file mode 100644 index 000000000..5ae8850f5 --- /dev/null +++ b/extensions/_exceptions.py @@ -0,0 +1,160 @@ +# 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 NovatenantNotFound(NotFound): + message = _("Novatenant %(_id)s could not be found") + + +class PortNotFound(NotFound): + message = _("Port %(port_id)s could not be found " \ + "on Network %(net_id)s") + + +class CredentialNotFound(NotFound): + message = _("Credential %(_id)s could not be found") + + +class QosNotFound(NotFound): + message = _("QoS %(_id)s could not be found") + + +""" + + +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/extensions/_faults.py b/extensions/_faults.py new file mode 100644 index 000000000..95ee9aed6 --- /dev/null +++ b/extensions/_faults.py @@ -0,0 +1,157 @@ +# 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", + 451: "CredentialNotFound", + 452: "QoSNotFound", + 453: "NovatenantNotFound", + 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 CredentialNotFound(webob.exc.HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the server did not find the Credential specified + in the HTTP request + + code: 460, title: Credential not Found + """ + code = 451 + title = 'Credential Not Found' + explanation = ('Unable to find a Credential with' + + ' the specified identifier.') + + +class QosNotFound(webob.exc.HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the server did not find the QoS specified + in the HTTP request + + code: 480, title: QoS not Found + """ + code = 452 + title = 'QoS Not Found' + explanation = ('Unable to find a QoS with' + + ' the specified identifier.') + + +class NovatenantNotFound(webob.exc.HTTPClientError): + """ + subclass of :class:`~HTTPClientError` + + This indicates that the server did not find the Novatenant specified + in the HTTP request + + code: 480, title: Nova tenant not Found + """ + code = 453 + title = 'Nova tenant Not Found' + explanation = ('Unable to find a Novatenant 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/extensions/_novatenant_view.py b/extensions/_novatenant_view.py new file mode 100644 index 000000000..3a1b3f8ca --- /dev/null +++ b/extensions/_novatenant_view.py @@ -0,0 +1,28 @@ + + +import os + + +def get_view_builder(req): + base_url = req.application_url + return ViewBuilder(base_url) + + +class ViewBuilder(object): + """ + ViewBuilder for novatenant, + 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_host(self, host_data): + """Return host description.""" + return dict(host_desc=host_data['host_desc']) + + def build_vif(self, vif_data): + """Return VIF description.""" + return dict(vif_desc=vif_data['vif_desc']) diff --git a/extensions/_pprofiles.py b/extensions/_pprofiles.py new file mode 100644 index 000000000..997cb26e2 --- /dev/null +++ b/extensions/_pprofiles.py @@ -0,0 +1,40 @@ +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 portprofile""" + return dict(portprofile=dict(id=portprofile_data['profile_id'])) + + def _build_detail(self, portprofile_data): + """Return a detailed info of a portprofile.""" + if (portprofile_data['assignment'] == None): + return dict(portprofile=dict(id=portprofile_data['profile_id'], + name=portprofile_data['profile_name'], + qos_name=portprofile_data['qos_name'])) + else: + return dict(portprofile=dict(id=portprofile_data['profile_id'], + name=portprofile_data['profile_name'], + qos_name=portprofile_data['qos_name'], + assignment=portprofile_data['assignment'])) diff --git a/extensions/_qos_view.py b/extensions/_qos_view.py new file mode 100644 index 000000000..877d82d0f --- /dev/null +++ b/extensions/_qos_view.py @@ -0,0 +1,35 @@ +def get_view_builder(req): + base_url = req.application_url + return ViewBuilder(base_url) + + +class ViewBuilder(object): + """ + ViewBuilder for QoS, + 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, qos_data, is_detail=False): + """Generic method used to generate a QoS entity.""" + print "qos_DATA:%s" % qos_data + if is_detail: + qos = self._build_detail(qos_data) + else: + qos = self._build_simple(qos_data) + return qos + + def _build_simple(self, qos_data): + """Return a simple model of a server.""" + return dict(qos=dict(id=qos_data['qos_id'])) + + def _build_detail(self, qos_data): + """Return a simple model of a server.""" + + return dict(qos=dict(id=qos_data['qos_id'], + name=qos_data['qos_name'], + description=qos_data['qos_desc'])) diff --git a/extensions/credential.py b/extensions/credential.py new file mode 100644 index 000000000..46f25b6f1 --- /dev/null +++ b/extensions/credential.py @@ -0,0 +1,159 @@ +# 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 + +from webob import exc +from extensions import _credential_view as credential_view +from extensions import _exceptions as exception +from extensions import _faults as faults + +from quantum.api import api_common as common +from quantum.common import wsgi +from quantum.common import extensions +from quantum.manager import QuantumManager + +LOG = logging.getLogger('quantum.api.credentials') + + +class Credential(object): + + def __init__(self): + pass + + def get_name(self): + return "Cisco Credential" + + def get_alias(self): + return "Cisco Credential" + + def get_description(self): + return "Credential include username and password" + + def get_namespace(self): + return "" + + def get_updated(self): + return "2011-07-25T13:25:27-06:00" + + def get_resources(self): + parent_resource = dict(member_name="tenant", + collection_name="extensions/csco/tenants") + + controller = CredentialController(QuantumManager.get_plugin()) + return [extensions.ResourceExtension('credentials', controller, + parent=parent_resource)] + + +class CredentialController(common.QuantumController): + """ credential API controller + based on QuantumController """ + + _credential_ops_param_list = [{ + 'param-name': 'credential_name', + 'required': True}, { + 'param-name': 'user_name', + 'required': True}, { + 'param-name': 'password', + 'required': True}] + + _serialization_metadata = { + "application/xml": { + "attributes": { + "credential": ["id", "name"], + }, + }, + } + + def __init__(self, plugin): + self._resource_name = 'credential' + super(CredentialController, self).__init__(plugin) + + def index(self, request, tenant_id): + """ Returns a list of credential 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 credentials. """ + credentials = self._plugin.get_all_credentials(tenant_id) + builder = credential_view.get_view_builder(request) + result = [builder.build(credential, is_detail)['credential'] + for credential in credentials] + return dict(credentials=result) + + def show(self, request, tenant_id, id): + """ Returns credential details for the given credential id """ + try: + credential = self._plugin.get_credential_details( + tenant_id, id) + builder = credential_view.get_view_builder(request) + #build response with details + result = builder.build(credential, True) + return dict(credentials=result) + except exception.CredentialNotFound as e: + return faults.Fault(faults.CredentialNotFound(e)) + + #return faults.Fault(e) + + def create(self, request, tenant_id): + """ Creates a new credential for a given tenant """ + #look for credential name in request + try: + req_params = \ + self._parse_request_params(request, + self._credential_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + credential = self._plugin.\ + create_credential(tenant_id, + req_params['credential_name'], + req_params['user_name'], + req_params['password']) + builder = credential_view.get_view_builder(request) + result = builder.build(credential) + return dict(credentials=result) + + def update(self, request, tenant_id, id): + """ Updates the name for the credential with the given id """ + try: + req_params = \ + self._parse_request_params(request, + self._credential_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + try: + credential = self._plugin.\ + rename_credential(tenant_id, + id, req_params['credential_name']) + + builder = credential_view.get_view_builder(request) + result = builder.build(credential, True) + return dict(credentials=result) + except exception.CredentialNotFound as e: + return faults.Fault(faults.CredentialNotFound(e)) + + def delete(self, request, tenant_id, id): + """ Destroys the credential with the given id """ + try: + self._plugin.delete_credential(tenant_id, id) + return exc.HTTPAccepted() + except exception.CredentialNotFound as e: + return faults.Fault(faults.CredentialNotFound(e)) + \ No newline at end of file diff --git a/extensions/novatenant.py b/extensions/novatenant.py new file mode 100644 index 000000000..91a48e8fd --- /dev/null +++ b/extensions/novatenant.py @@ -0,0 +1,161 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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. + +from webob import exc + +from extensions import _novatenant_view as novatenant_view +from extensions import _exceptions as exception +from extensions import _faults as faults + +from quantum.api import api_common as common +from quantum.common import wsgi +from quantum.common import extensions +from quantum.manager import QuantumManager + + +class Novatenant(object): + + def __init__(self): + pass + + def get_name(self): + return "Cisco Nova Tenant" + + def get_alias(self): + return "Cisco Nova Tenant" + + def get_description(self): + return "novatenant resource is used by nova side to invoke quantum api" + + def get_namespace(self): + return "" + + def get_updated(self): + return "2011-08-09T13:25:27-06:00" + + def get_resources(self): + parent_resource = dict(member_name="tenant", + collection_name="extensions/csco/tenants") + member_actions = {'get_host': "PUT", + 'get_instance_port': "PUT"} + controller = NovatenantsController(QuantumManager.get_plugin()) + return [extensions.ResourceExtension('novatenants', controller, + parent=parent_resource, + member_actions=member_actions)] + + +class NovatenantsController(common.QuantumController): + """ Novatenant API controller + based on QuantumController """ + + _Novatenant_ops_param_list = [{ + 'param-name': 'novatenant_name', + 'required': True}] + + _get_host_ops_param_list = [{ + 'param-name': 'instance_id', + 'required': True}, { + 'param-name': 'instance_desc', + 'required': True}] + + _serialization_metadata = { + "application/xml": { + "attributes": { + "novatenant": ["id", "name"], + }, + }, + } + + def __init__(self, plugin): + self._resource_name = 'novatenant' + super(NovatenantsController, self).__init__(plugin) + + def index(self, request, tenant_id): + """ Returns a list of novatenant ids """ + return "novatenant is a dummy resource" + + def _items(self, request, tenant_id, is_detail): + """ Returns a list of novatenants. """ + return "novatenant is a dummy resource" + + def show(self, request, tenant_id, id): + """ Returns novatenant details for the given novatenant id """ + return "novatenant is a dummy resource" + + def create(self, request, tenant_id): + """ Creates a new novatenant for a given tenant """ + return "novatenant is a dummy resource" + + def update(self, request, tenant_id, id): + """ Updates the name for the novatenant with the given id """ + return "novatenant is a dummy resource" + + def delete(self, request, tenant_id, id): + """ Destroys the Novatenant with the given id """ + return "novatenant is a dummy resource" + + #added for cisco's extension + def get_host(self, request, tenant_id, id): + content_type = request.best_match_content_type() + print "Content type:%s" % content_type + + try: + req_params = \ + self._parse_request_params(request, + self._get_host_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + instance_id = req_params['instance_id'] + + instance_desc = req_params['instance_desc'] + try: + host = self._plugin.get_host(tenant_id, instance_id, instance_desc) + builder = novatenant_view.get_view_builder(request) + result = builder.build_host(host) + return result + #return exc.HTTPAccepted() + except exception.NovatenantNotFound as e: + return faults.Fault(faults.NovatenantNotFound(e)) + except exception.PortNotFound as e: + return faults.Fault(faults.PortNotFound(e)) + + #added for Cisco extension + def get_instance_port(self, request, tenant_id, id): + content_type = request.best_match_content_type() + print "Content type:%s" % content_type + + try: + req_params = \ + self._parse_request_params(request, + self._get_host_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + instance_id = req_params['instance_id'] + + instance_desc = req_params['instance_desc'] + try: + vif = self._plugin. \ + get_instance_port(tenant_id, instance_id, instance_desc) + builder = novatenant_view.get_view_builder(request) + result = builder.build_vif(vif) + return result + + return exc.HTTPAccepted() + except exception.NovatenantNotFound as e: + return faults.Fault(faults.NovatenantNotFound(e)) + except exception.PortNotFound as e: + return faults.Fault(faults.PortNotFound(e)) diff --git a/extensions/portprofile.py b/extensions/portprofile.py new file mode 100644 index 000000000..7cd2b3633 --- /dev/null +++ b/extensions/portprofile.py @@ -0,0 +1,207 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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. + +from webob import exc + +from extensions import _pprofiles as pprofiles_view +from extensions import _exceptions as exception +from extensions import _faults as faults + +from quantum.api import api_common as common +from quantum.common import wsgi +from quantum.common import extensions +from quantum.manager import QuantumManager + + +class Portprofile(object): + + def __init__(self): + pass + + def get_name(self): + return "Cisco Port Profile" + + def get_alias(self): + return "Cisco Port Profile" + + def get_description(self): + return "Portprofile include QoS information" + + def get_namespace(self): + return "" + + def get_updated(self): + return "2011-07-23T13:25:27-06:00" + + def get_resources(self): + parent_resource = dict(member_name="tenant", + collection_name="extensions/csco/tenants") + member_actions = {'associate_portprofile': "PUT", + 'disassociate_portprofile': "POST"} + controller = PortprofilesController(QuantumManager.get_plugin()) + return [extensions.ResourceExtension('portprofiles', controller, + parent=parent_resource, + member_actions=member_actions)] + + +class PortprofilesController(common.QuantumController): + """ portprofile API controller + based on QuantumController """ + + _portprofile_ops_param_list = [{ + 'param-name': 'portprofile_name', + 'required': True}, { + 'param-name': 'qos_name', + '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(PortprofilesController, 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['qos_name']) + 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, 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, + 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, 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, 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/extensions/qos.py b/extensions/qos.py new file mode 100644 index 000000000..5351cc00b --- /dev/null +++ b/extensions/qos.py @@ -0,0 +1,154 @@ +# 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 + +from webob import exc +from extensions import _qos_view as qos_view +from extensions import _exceptions as exception +from extensions import _faults as faults + +from quantum.api import api_common as common +from quantum.common import wsgi +from quantum.common import extensions +from quantum.manager import QuantumManager + +LOG = logging.getLogger('quantum.api.qoss') + + +class Qos(object): + + def __init__(self): + pass + + def get_name(self): + return "Cisco qos" + + def get_alias(self): + return "Cisco qos" + + def get_description(self): + return "qos include username and password" + + def get_namespace(self): + return "" + + def get_updated(self): + return "2011-07-25T13:25:27-06:00" + + def get_resources(self): + parent_resource = dict(member_name="tenant", + collection_name="extensions/csco/tenants") + + controller = QosController(QuantumManager.get_plugin()) + return [extensions.ResourceExtension('qoss', controller, + parent=parent_resource)] + + +class QosController(common.QuantumController): + """ qos API controller + based on QuantumController """ + + _qos_ops_param_list = [{ + 'param-name': 'qos_name', + 'required': True}, { + 'param-name': 'qos_desc', + 'required': True}] + _serialization_metadata = { + "application/xml": { + "attributes": { + "qos": ["id", "name"], + }, + }, + } + + def __init__(self, plugin): + self._resource_name = 'qos' + super(QosController, self).__init__(plugin) + + def index(self, request, tenant_id): + """ Returns a list of qos 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 qoss. """ + qoss = self._plugin.get_all_qoss(tenant_id) + builder = qos_view.get_view_builder(request) + result = [builder.build(qos, is_detail)['qos'] + for qos in qoss] + return dict(qoss=result) + + def show(self, request, tenant_id, id): + """ Returns qos details for the given qos id """ + try: + qos = self._plugin.get_qos_details( + tenant_id, id) + builder = qos_view.get_view_builder(request) + #build response with details + result = builder.build(qos, True) + return dict(qoss=result) + except exception.QosNotFound as e: + return faults.Fault(faults.QosNotFound(e)) + + #return faults.Fault(e) + + def create(self, request, tenant_id): + """ Creates a new qos for a given tenant """ + #look for qos name in request + try: + req_params = \ + self._parse_request_params(request, + self._qos_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + qos = self._plugin.\ + create_qos(tenant_id, + req_params['qos_name'], + req_params['qos_desc']) + builder = qos_view.get_view_builder(request) + result = builder.build(qos) + return dict(qoss=result) + + def update(self, request, tenant_id, id): + """ Updates the name for the qos with the given id """ + try: + req_params = \ + self._parse_request_params(request, + self._qos_ops_param_list) + except exc.HTTPError as e: + return faults.Fault(e) + try: + qos = self._plugin.\ + rename_qos(tenant_id, + id, req_params['qos_name']) + + builder = qos_view.get_view_builder(request) + result = builder.build(qos, True) + return dict(qoss=result) + except exception.QosNotFound as e: + return faults.Fault(faults.QosNotFound(e)) + + def delete(self, request, tenant_id, id): + """ Destroys the qos with the given id """ + try: + self._plugin.delete_qos(tenant_id, id) + return exc.HTTPAccepted() + except exception.QosNotFound as e: + return faults.Fault(faults.QosNotFound(e)) diff --git a/quantum/plugins.ini b/quantum/plugins.ini index 307d2b48d..58db5dbc1 100644 --- a/quantum/plugins.ini +++ b/quantum/plugins.ini @@ -1,3 +1,4 @@ [PLUGIN] -# Quantum plugin provider module -provider = quantum.plugins.SamplePlugin.FakePlugin +provider = quantum.plugins.cisco.l2network_plugin.L2Network +#provider = quantum.plugins.cisco.CiscoPlugin.CiscoPaloPlugin2 +#provider = quantum.plugins.SamplePlugin.FakePlugin diff --git a/quantum/plugins/cisco/CiscoPlugin.py b/quantum/plugins/cisco/CiscoPlugin.py new file mode 100644 index 000000000..bed81abf7 --- /dev/null +++ b/quantum/plugins/cisco/CiscoPlugin.py @@ -0,0 +1,562 @@ +from quantum.common import exceptions as exc +from extensions import _exceptions as extexc + + +class CiscoPaloPlugin2(object): + """ + This plugin has internal data structure + derived from quantum fakeplugin + """ + + #static data for networks and ports + _port_dict_1 = { + 1: {'port-id': 1, + 'port-state': 'DOWN', + 'attachment': None, + 'portprofile': None}, + 2: {'port-id': 2, + 'port-state': 'UP', + 'attachment': None, + 'portprofile': None}} + _port_dict_2 = { + 1: {'port-id': 1, + 'port-state': 'UP', + 'attachment': 'SomeFormOfVIFID', + 'portprofile': '001'}, + 2: {'port-id': 2, + 'port-state': 'DOWN', + 'attachment': None, + 'portprofile': '001'}} + _networks = {'001': + { + 'net-id': '001', + 'net-name': 'pippotest', + 'net-ports': _port_dict_1}, + '002': + { + 'net-id': '002', + 'net-name': 'cicciotest', + 'net-ports': _port_dict_2}} + _portprofiles = {'001': + { + 'profile_id': '001', + 'profile_name': 'pprofiletest', + 'assignment': ['1', '2'], + 'qos_name': '001'}, + '002': + { + 'profile_id': '002', + 'profile_name': 'cicciotest', + 'qos_name': '002', + 'assignment': None}} + + _credentials = {'001': + { + 'credential_id': '001', + 'credential_name': 'cred1', + 'user_name': 'ying', + 'password': 'yingTest'}, + '002': + { + 'credential_id': '002', + 'credential_name': 'cred2', + 'user_name': 'admin', + 'password': 'adminTest'}} + _qoss = {'001': + { + 'qos_id': '001', + 'qos_name': 'silver', + 'qos_desc': {'pps':170, 'TTL':20}}, + '002': + { + 'qos_id': '002', + 'qos_name': 'gold', + 'qos_desc': {'pps':340, 'TTL':10}}} + + _host = {'host_desc': { + "host_key1": "host_value1", + "host_key2": "host_value2"}} + _vif = {'vif_desc': { + "vif_key1": "vif_value1", + "vif_key2": "vif_value2"} + } + + + supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile", "Cisco qos", "Cisco Nova Tenant"] + + + """ + def supports_extension(self, extension): + #return extension.get_alias() == "Cisco Port Profile" + return extension.get_alias() == "Cisco Credential" + """ + def __init__(self): + CiscoPaloPlugin2._net_counter = \ + len(CiscoPaloPlugin2._networks) + + CiscoPaloPlugin2._profile_counter = \ + len(CiscoPaloPlugin2._portprofiles) + + CiscoPaloPlugin2._credential_counter = \ + len(CiscoPaloPlugin2._credentials) + + CiscoPaloPlugin2._qos_counter = \ + len(CiscoPaloPlugin2._qoss) + def _get_network(self, tenant_id, network_id): + + network = CiscoPaloPlugin2._networks.get(network_id) + if not network: + raise exc.NetworkNotFound(net_id=network_id) + return network + + + + def _get_credential(self, tenant_id, credential_id): + credential = CiscoPaloPlugin2._credentials.get(credential_id) + if not credential: + raise extexc.CredentialNotFound(credential_id=credential_id) + return credential + + def _get_qos(self, tenant_id, qos_id): + qos = CiscoPaloPlugin2._qoss.get(qos_id) + if not qos: + raise extexc.QosNotFound(qos_id=qos_id) + return qos + + def _get_port(self, tenant_id, network_id, port_id): + net = self._get_network(tenant_id, network_id) + port = net['net-ports'].get(int(port_id)) + if not port: + raise exc.PortNotFound(net_id=network_id, port_id=port_id) + return port + + def _validate_port_state(self, port_state): + if port_state.upper() not in ('UP', 'DOWN'): + raise exc.StateInvalid(port_state=port_state) + return True + + def _validate_attachment(self, tenant_id, network_id, port_id, + remote_interface_id): + network = self._get_network(tenant_id, network_id) + for port in network['net-ports'].values(): + if port['attachment'] == remote_interface_id: + raise exc.AlreadyAttached(net_id=network_id, + port_id=port_id, + att_id=port['attachment'], + att_port_id=port['port-id']) + + def get_all_networks(self, tenant_id): + """ + Returns a dictionary containing all + for + the specified tenant. + """ + print("get_all_networks() called\n") + return CiscoPaloPlugin2._networks.values() + + def get_network_details(self, tenant_id, net_id): + """ + retrieved a list of all the remote vifs that + are attached to the network + """ + print("get_network_details() called\n") + return self._get_network(tenant_id, net_id) + + def create_network(self, tenant_id, net_name): + """ + Creates a new Virtual Network, and assigns it + a symbolic name. + """ + print("create_network() called\n") + CiscoPaloPlugin2._net_counter += 1 + new_net_id = ("0" * (3 - len(str(CiscoPaloPlugin2._net_counter)))) + \ + str(CiscoPaloPlugin2._net_counter) + print new_net_id + new_net_dict = {'net-id': new_net_id, + 'net-name': net_name, + 'net-ports': {}} + CiscoPaloPlugin2._networks[new_net_id] = new_net_dict + # return network_id of the created network + return new_net_dict + + def delete_network(self, tenant_id, net_id): + """ + Deletes the network with the specified network identifier + belonging to the specified tenant. + """ + print("delete_network() called\n") + net = CiscoPaloPlugin2._networks.get(net_id) + # Verify that no attachments are plugged into the network + if net: + if net['net-ports']: + for port in net['net-ports'].values(): + if port['attachment']: + raise exc.NetworkInUse(net_id=net_id) + CiscoPaloPlugin2._networks.pop(net_id) + return net + # Network not found + raise exc.NetworkNotFound(net_id=net_id) + + def rename_network(self, tenant_id, net_id, new_name): + """ + Updates the symbolic name belonging to a particular + Virtual Network. + """ + print("rename_network() called\n") + net = self._get_network(tenant_id, net_id) + net['net-name'] = new_name + return net + + def _get_portprofile(self, tenant_id, portprofile_id): + portprofile = CiscoPaloPlugin2._portprofiles.get(portprofile_id) + if not portprofile: + raise extexc.PortprofileNotFound(portprofile_id=portprofile_id) + return portprofile + + def get_all_portprofiles(self, tenant_id): + """ + Returns a dictionary containing all + for + the specified tenant. + """ + print("get_all_portprofiles() called\n") + return CiscoPaloPlugin2._portprofiles.values() + + def get_portprofile_details(self, tenant_id, profile_id): + """ + retrieved a list of all the remote vifs that + are attached to the portprofile + """ + print("get_portprofile_details() called\n") + return self._get_portprofile(tenant_id, profile_id) + + def create_portprofile(self, tenant_id, profile_name, vlan_id): + """ + Creates a new Virtual portprofile, and assigns it + a symbolic name. + """ + print("create_portprofile() called\n") + CiscoPaloPlugin2._profile_counter += 1 + new_profile_id = ("0" * \ + (3 - \ + len(str(CiscoPaloPlugin2._profile_counter)))) + \ + str(CiscoPaloPlugin2._profile_counter) + print new_profile_id + new_profile_dict = {'profile_id': new_profile_id, + 'profile_name': profile_name, + 'qos_name': vlan_id, + 'assignment': None} + CiscoPaloPlugin2._portprofiles[new_profile_id] = new_profile_dict + # return portprofile_id of the created portprofile + return new_profile_dict + + def delete_portprofile(self, tenant_id, profile_id): + """ + Deletes the portprofile with the specified portprofile identifier + belonging to the specified tenant. + """ + print("delete_portprofile() called\n") + profile = CiscoPaloPlugin2._portprofiles.get(profile_id) + # Verify that no attachments are plugged into the portprofile + if profile: + CiscoPaloPlugin2._portprofiles.pop(profile_id) + return profile + # portprofile not found + raise extexc.PortprofileNotFound(profile_id=profile_id) + + def rename_portprofile(self, tenant_id, profile_id, new_name): + """ + Updates the symbolic name belonging to a particular + Virtual portprofile. + """ + print("rename_portprofile() called\n") + profile = self._get_portprofile(tenant_id, profile_id) + profile['profile_name'] = new_name + return profile + + + + def associate_portprofile(self, tenant_id, net_id, port_id, pprofile_id): + """ + Assign portprofile to the specified port on the + specified Virtual Network. + """ + print("assign_portprofile() called\n") + print("net_id " + net_id) + # Validate attachment + #self._validate_attachment(tenant_id, net_id, port_id, + # remote_interface_id) + #TODO: modify the exception + port = self._get_port(tenant_id, net_id, port_id) + if (not port['portprofile'] == None): + raise exc.PortInUse(net_id=net_id, port_id=port_id, + att_id=port['portprofile']) + port['portprofile'] = pprofile_id + + def disassociate_portprofile(self, tenant_id, net_id, port_id, portprofile_id): + """ + De-assign a portprofile from the specified port on the + specified Virtual Network. + """ + print("deassign_portprofile() called\n") + #print("*******net_id is "+net_id) + port = self._get_port(tenant_id, net_id, port_id) + + port['portprofile'] = None + #TODO: + #modify assignment[portprofile_id] to remove this port + + #TODO: add new data structure to + #hold all the assignment for a specific portprofile + def get_portprofile_assignment(self, tenant_id, net_id, port_id): + print("get portprofile assignment called\n") + port = self._get_port(tenant_id, net_id, port_id) + ppid = port['portprofile'] + if (ppid == None): + print("***no portprofile attached") + return "no portprofile attached" + else: + print("***attached portprofile id is " + ppid) + return ("attached portprofile " + ppid) + + + def get_all_credentials(self, tenant_id): + """ + Returns a dictionary containing all + for + the specified tenant. + """ + print("get_all_credentials() called\n") + return CiscoPaloPlugin2._credentials.values() + + def get_credential_details(self, tenant_id, credential_id): + """ + retrieved a list of all the remote vifs that + are attached to the credential + """ + print("get_credential_details() called\n") + return self._get_credential(tenant_id, credential_id) + + def create_credential(self, tenant_id, credential_name, user_name, password): + """ + Creates a new Virtual credential, and assigns it + a symbolic name. + """ + print("create_credential() called\n") + CiscoPaloPlugin2._credential_counter += 1 + new_credential_id = ("0" * \ + (3 - \ + len(str(CiscoPaloPlugin2._credential_counter)))) + \ + str(CiscoPaloPlugin2._credential_counter) + print new_credential_id + new_credential_dict = {'credential_id': new_credential_id, + 'credential_name': credential_name, + 'user_name': user_name, + 'password': password} + CiscoPaloPlugin2._credentials[new_credential_id] = new_credential_dict + # return credential_id of the created credential + return new_credential_dict + + def delete_credential(self, tenant_id, credential_id): + """ + Deletes the credential with the specified credential identifier + belonging to the specified tenant. + """ + print("delete_credential() called\n") + credential = CiscoPaloPlugin2._credentials.get(credential_id) + + if credential: + CiscoPaloPlugin2._credentials.pop(credential_id) + return credential + # credential not found + raise extexc.CredentialNotFound(credential_id=credential_id) + + def rename_credential(self, tenant_id, credential_id, new_name): + """ + Updates the symbolic name belonging to a particular + Virtual credential. + """ + print("rename_credential() called\n") + credential = self._get_credential(tenant_id, credential_id) + credential['credential_name'] = new_name + return credential + + + def get_all_qoss(self, tenant_id): + """ + Returns a dictionary containing all + for + the specified tenant. + """ + print("get_all_qoss() called\n") + return CiscoPaloPlugin2._qoss.values() + + def get_qos_details(self, tenant_id, qos_id): + """ + retrieved a list of all the remote vifs that + are attached to the qos + """ + print("get_qos_details() called\n") + return self._get_qos(tenant_id, qos_id) + + def create_qos(self, tenant_id, qos_name, qos_desc): + """ + Creates a new Virtual qos, and assigns it + a symbolic name. + """ + print("create_qos() called\n") + CiscoPaloPlugin2._qos_counter += 1 + new_qos_id = ("0" * \ + (3 - \ + len(str(CiscoPaloPlugin2._qos_counter)))) + \ + str(CiscoPaloPlugin2._qos_counter) + print new_qos_id + new_qos_dict = {'qos_id': new_qos_id, + 'qos_name': qos_name, + 'qos_desc': qos_desc} + + print("************************") + print("test dictionary data") + print(qos_desc['TTL']) + print("************************") + + CiscoPaloPlugin2._qoss[new_qos_id] = new_qos_dict + # return qos_id of the created qos + return new_qos_dict + + def delete_qos(self, tenant_id, qos_id): + """ + Deletes the qos with the specified qos identifier + belonging to the specified tenant. + """ + print("delete_qos() called\n") + qos = CiscoPaloPlugin2._qoss.get(qos_id) + # Verify that no attachments are plugged into the qos + if qos: + CiscoPaloPlugin2._qoss.pop(qos_id) + return qos + # qos not found + raise extexc.QosNotFound(qos_id=qos_id) + + def rename_qos(self, tenant_id, qos_id, new_name): + """ + Updates the symbolic name belonging to a particular + Virtual qos. + """ + print("rename_qos() called\n") + qos = self._get_qos(tenant_id, qos_id) + qos['qos_name'] = new_name + return qos + + + + + def get_all_ports(self, tenant_id, net_id): + """ + Retrieves all port identifiers belonging to the + specified Virtual Network. + """ + print("get_all_ports() called\n") + network = self._get_network(tenant_id, net_id) + ports_on_net = network['net-ports'].values() + return ports_on_net + + def get_port_details(self, tenant_id, net_id, port_id): + """ + This method allows the user to retrieve a remote interface + that is attached to this particular port. + """ + print("get_port_details() called\n") + return self._get_port(tenant_id, net_id, port_id) + + def create_port(self, tenant_id, net_id, port_state=None): + """ + Creates a port on the specified Virtual Network. + """ + print("create_port() called\n") + net = self._get_network(tenant_id, net_id) + # check port state + # TODO(salvatore-orlando): Validate port state in API? + self._validate_port_state(port_state) + ports = net['net-ports'] + new_port_id = max(ports.keys()) + 1 + new_port_dict = {'port-id': new_port_id, + 'port-state': port_state, + 'attachment': None, + 'portprofile': None} + ports[new_port_id] = new_port_dict + return new_port_dict + + def update_port(self, tenant_id, net_id, port_id, port_state): + """ + Updates the state of a port on the specified Virtual Network. + """ + print("create_port() called\n") + port = self._get_port(tenant_id, net_id, port_id) + self._validate_port_state(port_state) + port['port-state'] = port_state + return port + + def delete_port(self, tenant_id, net_id, port_id): + """ + Deletes a port on a specified Virtual Network, + if the port contains a remote interface attachment, + the remote interface is first un-plugged and then the port + is deleted. + """ + print("delete_port() called\n") + net = self._get_network(tenant_id, net_id) + port = self._get_port(tenant_id, net_id, port_id) + if port['attachment']: + raise exc.PortInUse(net_id=net_id, port_id=port_id, + att_id=port['attachment']) + try: + net['net-ports'].pop(int(port_id)) + except KeyError: + raise exc.PortNotFound(net_id=net_id, port_id=port_id) + + def get_interface_details(self, tenant_id, net_id, port_id): + print("get interface detail called\n") + port = self._get_port(tenant_id, net_id, port_id) + vid = port['attachment'] + if (vid == None): + print("***no interface is attached") + return "no interface attached" + else: + print("***interface id is " + vid) + return ("attached interface " + vid) + + def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): + """ + Attaches a remote interface to the specified port on the + specified Virtual Network. + """ + print("plug_interface() called\n") + # Validate attachment + self._validate_attachment(tenant_id, net_id, port_id, + remote_interface_id) + port = self._get_port(tenant_id, net_id, port_id) + if port['attachment']: + raise exc.PortInUse(net_id=net_id, port_id=port_id, + att_id=port['attachment']) + port['attachment'] = remote_interface_id + + def unplug_interface(self, tenant_id, net_id, port_id): + """ + Detaches a remote interface from the specified port on the + specified Virtual Network. + """ + print("unplug_interface() called\n") + port = self._get_port(tenant_id, net_id, port_id) + # TODO(salvatore-orlando): + # Should unplug on port without attachment raise an Error? + port['attachment'] = None + + def get_host(self, tenant_id, instance_id, instance_desc): + print("associate an instance to a port....") + print("get key2: " + instance_desc['key2']) + return CiscoPaloPlugin2._host + def get_instance_port(self, tenant_id, instance_id, instance_desc): + print("get instance associated port....") + print("get key1: " + instance_desc['key1']) + return CiscoPaloPlugin2._vif \ No newline at end of file diff --git a/quantum/plugins/cisco/conf/db_conn.ini b/quantum/plugins/cisco/conf/db_conn.ini index 4a5d7e371..fdc7ace15 100644 --- a/quantum/plugins/cisco/conf/db_conn.ini +++ b/quantum/plugins/cisco/conf/db_conn.ini @@ -1,5 +1,5 @@ [DATABASE] name = quantum_l2network -user = -pass = -host = +user =root +pass =nova +host =localhost diff --git a/quantum/plugins/cisco/conf/l2network_plugin.ini b/quantum/plugins/cisco/conf/l2network_plugin.ini index 3a740a971..f51c1ae33 100644 --- a/quantum/plugins/cisco/conf/l2network_plugin.ini +++ b/quantum/plugins/cisco/conf/l2network_plugin.ini @@ -1,6 +1,6 @@ [VLANS] -vlan_start= -vlan_end= +vlan_start=100 +vlan_end=300 vlan_name_prefix=q- [PORTS] diff --git a/quantum/plugins/cisco/conf/plugins.ini b/quantum/plugins/cisco/conf/plugins.ini index 8b4b476a0..a4b62c890 100644 --- a/quantum/plugins/cisco/conf/plugins.ini +++ b/quantum/plugins/cisco/conf/plugins.ini @@ -1,3 +1,3 @@ [PLUGINS] -ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin +#ucs_plugin=quantum.plugins.cisco.ucs.cisco_ucs_plugin.UCSVICPlugin #nexus_plugin=quantum.plugins.cisco.nexus.cisco_nexus_plugin.NexusPlugin diff --git a/test_scripts/__init__.py b/test_scripts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_scripts/miniclient.py b/test_scripts/miniclient.py new file mode 100644 index 000000000..fb1ebc8fe --- /dev/null +++ b/test_scripts/miniclient.py @@ -0,0 +1,98 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Citrix Systems +# 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. + +import httplib +import socket +import urllib + +class MiniClient(object): + + """A base client class - derived from Glance.BaseClient""" + + action_prefix = '/v0.1/tenants/{tenant_id}' + + def __init__(self, host, port, use_ssl): + """ + Creates a new client to some service. + + :param host: The host where service resides + :param port: The port where service resides + :param use_ssl: Should we use HTTPS? + """ + self.host = host + self.port = port + self.use_ssl = use_ssl + self.connection = None + + def get_connection_type(self): + """ + Returns the proper connection type + """ + if self.use_ssl: + return httplib.HTTPSConnection + else: + return httplib.HTTPConnection + + def do_request(self, tenant, method, action, body=None, + headers=None, params=None): + """ + Connects to the server and issues a request. + Returns the result data, or raises an appropriate exception if + HTTP status code is not 2xx + + :param method: HTTP method ("GET", "POST", "PUT", etc...) + :param body: string of data to send, or None (default) + :param headers: mapping of key/value pairs to add as headers + :param params: dictionary of key/value pairs to add to append + to action + + """ + action = MiniClient.action_prefix + action + action = action.replace('{tenant_id}',tenant) + if type(params) is dict: + action += '?' + urllib.urlencode(params) + + try: + connection_type = self.get_connection_type() + headers = headers or {} + + # Open connection and send request + c = connection_type(self.host, self.port) + c.request(method, action, body, headers) + res = c.getresponse() + status_code = self.get_status_code(res) + if status_code in (httplib.OK, + httplib.CREATED, + httplib.ACCEPTED, + httplib.NO_CONTENT): + return res + else: + raise Exception("Server returned error: %s" % res.read()) + + except (socket.error, IOError), e: + raise Exception("Unable to connect to " + "server. Got error: %s" % e) + + def get_status_code(self, response): + """ + Returns the integer status code from the response, which + can be either a Webob.Response (used in testing) or httplib.Response + """ + if hasattr(response, 'status_int'): + return response.status_int + else: + return response.status \ No newline at end of file diff --git a/test_scripts/tests.py b/test_scripts/tests.py new file mode 100644 index 000000000..589d9da22 --- /dev/null +++ b/test_scripts/tests.py @@ -0,0 +1,150 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Citrix Systems +# 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. + +import gettext + +gettext.install('quantum', unicode=1) + +from miniclient import MiniClient +from quantum.common.wsgi import Serializer + +HOST = '127.0.0.1' +PORT = 9696 +USE_SSL = False +TENANT_ID = 'totore' + +test_network_data = \ + {'network': {'network-name': 'test' }} + +def print_response(res): + content = res.read() + print "Status: %s" %res.status + print "Content: %s" %content + return content + +def test_list_networks_and_ports(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "TEST LIST NETWORKS AND PORTS -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - List All Networks" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + print_response(res) + print "--> Step 2 - Details for Network 001" + res = client.do_request(TENANT_ID,'GET', "/networks/001." + format) + print_response(res) + print "--> Step 3 - Ports for Network 001" + res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format) + print_response(res) + print "--> Step 4 - Details for Port 1" + res = client.do_request(TENANT_ID,'GET', "/networks/001/ports/1." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_create_network(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "TEST CREATE NETWORK -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - Create Network" + content_type = "application/" + format + body = Serializer().serialize(test_network_data, content_type) + res = client.do_request(TENANT_ID,'POST', "/networks." + format, body=body) + print_response(res) + print "--> Step 2 - List All Networks" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_rename_network(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST RENAME NETWORK -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - Retrieve network" + res = client.do_request(TENANT_ID,'GET', "/networks/001." + format) + print_response(res) + print "--> Step 2 - Rename network to 'test_renamed'" + test_network_data['network']['network-name'] = 'test_renamed' + body = Serializer().serialize(test_network_data, content_type) + res = client.do_request(TENANT_ID,'PUT', "/networks/001." + format, body=body) + print_response(res) + print "--> Step 2 - Retrieve network (again)" + res = client.do_request(TENANT_ID,'GET', "/networks/001." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + +def test_delete_network(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + content_type = "application/" + format + print "TEST DELETE NETWORK -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - List All Networks" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + content = print_response(res) + network_data = Serializer().deserialize(content, content_type) + print network_data + net_id = network_data['networks'][0]['id'] + print "--> Step 2 - Delete network %s" %net_id + res = client.do_request(TENANT_ID,'DELETE', + "/networks/" + net_id + "." + format) + print_response(res) + print "--> Step 3 - List All Networks (Again)" + res = client.do_request(TENANT_ID,'GET', "/networks." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def test_create_port(format = 'xml'): + client = MiniClient(HOST, PORT, USE_SSL) + print "TEST CREATE PORT -- FORMAT:%s" %format + print "----------------------------" + print "--> Step 1 - List Ports for network 001" + res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format) + print_response(res) + print "--> Step 2 - Create Port for network 001" + res = client.do_request(TENANT_ID,'POST', "/networks/001/ports." + format) + print_response(res) + print "--> Step 3 - List Ports for network 001 (again)" + res = client.do_request(TENANT_ID,'GET', "/networks/001/ports." + format) + print_response(res) + print "COMPLETED" + print "----------------------------" + + +def main(): + test_list_networks_and_ports('xml') + test_list_networks_and_ports('json') + test_create_network('xml') + test_create_network('json') + test_rename_network('xml') + test_rename_network('json') + # NOTE: XML deserializer does not work properly + # disabling XML test - this is NOT a server-side issue + #test_delete_network('xml') + test_delete_network('json') + test_create_port('xml') + test_create_port('json') + + pass + + +# Standard boilerplate to call the main() function. +if __name__ == '__main__': + main() \ No newline at end of file