]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Adding serialization/deserilization for network resources.
authorSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Fri, 27 May 2011 16:52:06 +0000 (17:52 +0100)
committerSalvatore Orlando <salvatore.orlando@eu.citrix.com>
Fri, 27 May 2011 16:52:06 +0000 (17:52 +0100)
Adding fake plugin

bin/quantum
quantum/api/networks.py
quantum/common/utils.py
quantum/common/wsgi.py
quantum/plugins.ini
quantum/plugins/SamplePlugin.py

index 0c4a5dd6cf6df87c0472ee966229f1bc19f2e273..0913c31c054aa63f1c50578e8846bab3dd34f800 100755 (executable)
@@ -22,9 +22,7 @@
 import gettext
 import optparse
 import os
-import re
 import sys
-import time
 
 
 possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
@@ -36,7 +34,6 @@ if os.path.exists(os.path.join(possible_topdir, 'quantum', '__init__.py')):
 gettext.install('quantum', unicode=1)
 
 from quantum import service 
-from quantum.common import wsgi
 from quantum.common import config
 
 def create_options(parser):
@@ -55,19 +52,10 @@ if __name__ == '__main__':
     (options, args) = config.parse_options(oparser)
 
     try:
-        print "HERE-1"
-        print sys.path
         service = service.serve_wsgi(service.QuantumApiService,
                                      options=options,
                                      args=args)
-        #version_conf, version_app = config.load_paste_app('quantumversion', options, args)
-        print "HERE-2"
         service.wait()
-        #api_conf, api_app = config.load_paste_app('quantum', options, args)
-        #server = wsgi.Server()
-        #server.start(version_app, int(version_conf['bind_port']), version_conf['bind_host'])
-        #server.start(api_app, int(api_conf['bind_port']), api_conf['bind_host'])
-        #server.wait()
     except RuntimeError, e:
         sys.exit("ERROR: %s" % e)
 
index 5700a199625e5682a6309b4074f45e2277188035..88d56fcb8be2b3cf01c8b680d634094d6a83d92e 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import base64
+import httplib
 import logging
-import traceback
 
 from webob import exc
 from xml.dom import minidom
 
 from quantum import manager
-from quantum import quantum_plugin_base
 from quantum.common import exceptions as exception
 from quantum.common import flags
 from quantum.common import wsgi
-from quantum import utils
-from quantum.api import api_common as common
 from quantum.api import faults
-import quantum.api
+from quantum.api.views import networks as networks_view
 
 LOG = logging.getLogger('quantum.api.networks')
 FLAGS = flags.FLAGS
@@ -37,168 +33,114 @@ FLAGS = flags.FLAGS
 class Controller(wsgi.Controller):
     """ Network API controller for Quantum API """
 
-    #TODO (salvatore-orlando): adjust metadata for quantum
+    _network_ops_param_list = [{
+        'param-name': 'network-name', 
+        'required': True},]
+    
     _serialization_metadata = {
         "application/xml": {
             "attributes": {
-                "server": ["id", "imageId", "name", "flavorId", "hostId",
-                           "status", "progress", "adminPass", "flavorRef",
-                           "imageRef"],
+                "network": ["id","name"],
                 "link": ["rel", "type", "href"],
             },
-            "dict_collections": {
-                "metadata": {"item_name": "meta", "item_key": "key"},
-            },
-            "list_collections": {
-                "public": {"item_name": "ip", "item_key": "addr"},
-                "private": {"item_name": "ip", "item_key": "addr"},
-            },
         },
     }
 
-    def __init__(self):
+    def __init__(self, plugin_conf_file=None):
         self._setup_network_manager()
         super(Controller, self).__init__()
 
+    def _parse_request_params(self, req, params):
+        results = {}
+        for param in params:
+            param_name = param['param-name']
+            # 1- parse request body
+            # 2- parse request headers
+            # prepend param name with a 'x-' prefix
+            param_value = req.headers.get("x-" + param_name, None)
+            # 3- parse request query parameters
+            if not param_value:
+                param_value = req.str_GET[param_name]
+            if not param_value and param['required']: 
+                msg = ("Failed to parse request. " +
+                       "Parameter: %(param)s not specified" % locals())
+                for line in msg.split('\n'):
+                    LOG.error(line)
+                raise exc.HTTPBadRequest(msg)
+            results[param_name]=param_value
+        return results             
+        
     def _setup_network_manager(self):
         self.network_manager=manager.QuantumManager().get_manager()
     
     def index(self, req, tenant_id):
         """ Returns a list of network names and ids """
         #TODO: this should be for a given tenant!!!
-        LOG.debug("HERE - Controller.index")
         return self._items(req, tenant_id, is_detail=False)
 
     def _items(self, req, tenant_id, is_detail):
         """ Returns a list of networks. """
-        test = self.network_manager.get_all_networks(tenant_id)
-        #builder = self._get_view_builder(req)
-        #servers = [builder.build(inst, is_detail)['server']
-        #        for inst in limited_list]
-        #return dict(servers=servers)
-        return test
+        networks = self.network_manager.get_all_networks(tenant_id)
+        builder = networks_view.get_view_builder(req)
+        result = [builder.build(network, is_detail)['network']
+                  for network in networks]
+        return dict(networks=result)
     
     def show(self, req, tenant_id, id):
         """ Returns network details by network id """
         try:
-            return "SHOW NETWORK %s FOR TENANT %s" %(id,tenant_id)
+            network = self.network_manager.get_network_details(
+                            tenant_id,id)
+            builder = networks_view.get_view_builder(req)
+            #build response with details
+            result = builder.build(network, True)
+            return dict(networks=result)
         except exception.NotFound:
             return faults.Fault(exc.HTTPNotFound())
 
-    def delete(self, req, id):
+    def create(self, req, tenant_id):
+        """ Creates a new network for a given tenant """
+        #look for network name in request
+        req_params = \
+            self._parse_request_params(req, self._network_ops_param_list)
+        network = self.network_manager.create_network(tenant_id, req_params['network-name'])
+        builder = networks_view.get_view_builder(req)
+        result = builder.build(network)
+        return dict(networks=result)
+
+    def update(self, req, tenant_id, id):
+        """ Updates the name for the network with the given id """
+        try:
+            network_name = req.headers['x-network-name']
+        except KeyError as e:
+            msg = ("Failed to create network. Got error: %(e)s" % locals())
+            for line in msg.split('\n'):
+                LOG.error(line)
+            raise exc.HTTPBadRequest(msg)            
+
+        network = self.network_manager.rename_network(tenant_id,
+                 id,network_name)
+        if not network:
+            raise exc.HTTPNotFound("Network %(id)s could not be found" % locals())
+        builder = networks_view.get_view_builder(req)
+        result = builder.build(network, True)
+        return dict(networks=result)
+
+
+    def delete(self, req, tenant_id, id):
         """ Destroys the network with the given id """
         try:
-            return "TEST NETWORK DELETE"
-        except exception.NotFound:
-            return faults.Fault(exc.HTTPNotFound())
+            network_name = req.headers['x-network-name']
+        except KeyError as e:
+            msg = ("Failed to create network. Got error: %(e)s" % locals())
+            for line in msg.split('\n'):
+                LOG.error(line)
+            raise exc.HTTPBadRequest(msg)            
+
+        network = self.network_manager.delete_network(tenant_id, id)
+        if not network:
+            raise exc.HTTPNotFound("Network %(id)s could not be found" % locals())
+
         return exc.HTTPAccepted()
 
-    def create(self, req):
-        """ Creates a new network for a given tenant """
-        #env = self._deserialize_create(req)
-        #if not env:
-        #    return faults.Fault(exc.HTTPUnprocessableEntity())
-        return "TEST NETWORK CREATE"
-
-    def _deserialize_create(self, request):
-        """
-        Deserialize a create request
-        Overrides normal behavior in the case of xml content
-        """
-        #if request.content_type == "application/xml":
-        #    deserializer = ServerCreateRequestXMLDeserializer()
-        #    return deserializer.deserialize(request.body)
-        #else:
-        #    return self._deserialize(request.body, request.get_content_type())
-        pass
-
-    def update(self, req, id):
-        """ Updates the name for the network wit the given id """
-        if len(req.body) == 0:
-            raise exc.HTTPUnprocessableEntity()
-
-        inst_dict = self._deserialize(req.body, req.get_content_type())
-        if not inst_dict:
-            return faults.Fault(exc.HTTPUnprocessableEntity())
 
-        try:
-            return "TEST NETWORK UPDATE"
-        except exception.NotFound:
-            return faults.Fault(exc.HTTPNotFound())
-        return exc.HTTPNoContent()
-
-
-class NetworkCreateRequestXMLDeserializer(object):
-    """
-    Deserializer to handle xml-formatted server create requests.
-
-    Handles standard server attributes as well as optional metadata
-    and personality attributes
-    """
-
-    def deserialize(self, string):
-        """Deserialize an xml-formatted server create request"""
-        dom = minidom.parseString(string)
-        server = self._extract_server(dom)
-        return {'server': server}
-
-    def _extract_server(self, node):
-        """Marshal the server attribute of a parsed request"""
-        server = {}
-        server_node = self._find_first_child_named(node, 'server')
-        for attr in ["name", "imageId", "flavorId"]:
-            server[attr] = server_node.getAttribute(attr)
-        metadata = self._extract_metadata(server_node)
-        if metadata is not None:
-            server["metadata"] = metadata
-        personality = self._extract_personality(server_node)
-        if personality is not None:
-            server["personality"] = personality
-        return server
-
-    def _extract_metadata(self, server_node):
-        """Marshal the metadata attribute of a parsed request"""
-        metadata_node = self._find_first_child_named(server_node, "metadata")
-        if metadata_node is None:
-            return None
-        metadata = {}
-        for meta_node in self._find_children_named(metadata_node, "meta"):
-            key = meta_node.getAttribute("key")
-            metadata[key] = self._extract_text(meta_node)
-        return metadata
-
-    def _extract_personality(self, server_node):
-        """Marshal the personality attribute of a parsed request"""
-        personality_node = \
-                self._find_first_child_named(server_node, "personality")
-        if personality_node is None:
-            return None
-        personality = []
-        for file_node in self._find_children_named(personality_node, "file"):
-            item = {}
-            if file_node.hasAttribute("path"):
-                item["path"] = file_node.getAttribute("path")
-            item["contents"] = self._extract_text(file_node)
-            personality.append(item)
-        return personality
-
-    def _find_first_child_named(self, parent, name):
-        """Search a nodes children for the first child with a given name"""
-        for node in parent.childNodes:
-            if node.nodeName == name:
-                return node
-        return None
-
-    def _find_children_named(self, parent, name):
-        """Return all of a nodes children who have the given name"""
-        for node in parent.childNodes:
-            if node.nodeName == name:
-                yield node
-
-    def _extract_text(self, node):
-        """Get the text field contained by the given node"""
-        if len(node.childNodes) == 1:
-            child = node.childNodes[0]
-            if child.nodeType == child.TEXT_NODE:
-                return child.nodeValue
-        return ""
index 1317bc1571b49ff787fbd1041166abfe24f86a7a..c80ac9cccdae1e47092cb7fd3f6a000afcf07e64 100644 (file)
@@ -71,13 +71,9 @@ def bool_from_string(subject):
 def import_class(import_str):
     """Returns a class from a string including module and class"""
     mod_str, _sep, class_str = import_str.rpartition('.')
-    print "MOD_STR:%s SEP:%s CLASS_STR:%s" %(mod_str, _sep, class_str)
     try:
         #mod_str = os.path.join(FLAGS.state_path, mod_str)
-        print "MODULE PATH:%s" %mod_str
-        print "CUR DIR:%s" %os.getcwd()
-        __import__(mod_str, level=2)
-        print "IO SONO QUI"
+        __import__(mod_str)
         return getattr(sys.modules[mod_str], class_str)
     except (ImportError, ValueError, AttributeError) as e:
         print e
@@ -193,8 +189,6 @@ def parse_isotime(timestr):
     return datetime.datetime.strptime(timestr, TIME_FORMAT)
 
 def getPluginFromConfig(file="config.ini"):
-        print "FILE:%s" %os.path.join(FLAGS.state_path, file)
-        print "Globals:%s" %globals()
         Config = ConfigParser.ConfigParser()
         Config.read(os.path.join(FLAGS.state_path, file))
         return Config.get("PLUGIN", "provider")
index 277b482d6eb23d25768dff1c73fc48e5fbeb495f..4caeab9abca2484d7811bf4b7accf70a7ca39280 100644 (file)
@@ -126,7 +126,7 @@ class Request(webob.Request):
 
         """
         parts = self.path.rsplit('.', 1)
-
+        LOG.debug("Request parts:%s",parts)
         if len(parts) > 1:
             format = parts[1]
             if format in ['json', 'xml']:
@@ -134,7 +134,7 @@ class Request(webob.Request):
 
         ctypes = ['application/json', 'application/xml']
         bm = self.accept.best_match(ctypes)
-
+        LOG.debug("BM:%s",bm)
         return bm or 'application/json'
 
     def get_content_type(self):
@@ -281,7 +281,7 @@ class Router(object):
           mapper.connect(None, "/svrlist", controller=sc, action="list")
 
           # Actions are all implicitly defined
-          mapper.resource("server", "servers", controller=sc)
+          mapper.resource("network", "networks", controller=nc)
 
           # Pointing to an arbitrary WSGI app.  You can specify the
           # {path_info:.*} parameter so the target app can be handed just that
@@ -349,6 +349,8 @@ class Controller(object):
 
         if type(result) is dict:
             content_type = req.best_match_content_type()
+            LOG.debug("Content type:%s",content_type)
+            LOG.debug("Result:%s",result)
             default_xmlns = self.get_default_xmlns(req)
             body = self._serialize(result, content_type, default_xmlns)
 
@@ -495,8 +497,9 @@ class Serializer(object):
         xmlns = metadata.get('xmlns', None)
         if xmlns:
             result.setAttribute('xmlns', xmlns)
-
+        LOG.debug("DATA:%s",data)
         if type(data) is list:
+            LOG.debug("TYPE IS LIST")
             collections = metadata.get('list_collections', {})
             if nodename in collections:
                 metadata = collections[nodename]
@@ -515,6 +518,7 @@ class Serializer(object):
                 node = self._to_xml_node(doc, metadata, singular, item)
                 result.appendChild(node)
         elif type(data) is dict:
+            LOG.debug("TYPE IS DICT")
             collections = metadata.get('dict_collections', {})
             if nodename in collections:
                 metadata = collections[nodename]
@@ -534,6 +538,7 @@ class Serializer(object):
                     result.appendChild(node)
         else:
             # Type is atom
+            LOG.debug("TYPE IS ATOM:%s",data)
             node = doc.createTextNode(str(data))
             result.appendChild(node)
         return result
index e6dc080d45f3322baf6758b858c83f22ce8329b0..307d2b48d2c7b167a10112cf1990eb0441ff579f 100644 (file)
@@ -1,3 +1,3 @@
 [PLUGIN]
 # Quantum plugin provider module
-provider = quantum.plugins.SamplePlugin.DummyDataPlugin
+provider = quantum.plugins.SamplePlugin.FakePlugin
index 5088b71fa397639880aac9f32fbd305e582f348a..431f5fbc70fbcaa4ec92be36e77d272f5eb0e690 100644 (file)
@@ -259,4 +259,162 @@ class DummyDataPlugin(object):
         # returns a list of all attached remote interfaces
         vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0", "/tenant1/networks/10/121/vif1.1"]
         return vifs_on_net
+    
+    
+class FakePlugin(object):
+    """
+    FakePlugin is a demo plugin that provides
+    in-memory data structures to aid in quantum
+    client/cli/api development
+    """
+    def __init__(self):
+        #add a first sample network on init
+        self._networks={'001':
+                        {
+                        'net-id':'001',
+                        'net-name':'pippotest'
+                        },
+                        '002':
+                        {
+                        'net-id':'002',
+                        'net-name':'cicciotest'
+                        }}
+        self._net_counter=len(self._networks)
+    
+    def get_all_networks(self, tenant_id):
+        """
+        Returns a dictionary containing all
+        <network_uuid, network_name> for
+        the specified tenant. 
+        """
+        print("get_all_networks() called\n")
+        return self._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._networks.get(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")
+        self._net_counter += 1
+        new_net_id=("0" * (3 - len(str(self._net_counter)))) + \
+                    str(self._net_counter)
+        print new_net_id
+        new_net_dict={'net-id':new_net_id,
+                      'net-name':net_name}
+        self._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 = self._networks.get(net_id)
+        if net:
+            self._networks.pop(net_id)
+            return net
+        return None
+    
+    
+    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._networks.get(net_id, None)
+        if net:
+            net['net-name']=new_name 
+            return net
+        return None
+    
+
+    #TODO - neeed to update methods from this point onwards    
+    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")
+        port_ids_on_net = ["2", "3", "4"]
+        return port_ids_on_net
+    
+    
+    def create_port(self, tenant_id, net_id):
+        """
+        Creates a port on the specified Virtual Network.
+        """
+        print("create_port() called\n")
+        #return the port id
+        return 201
+    
+    
+    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")
+    
+    
+    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")
+        #returns the remote interface UUID
+        return "/tenant1/networks/net_id/portid/vif2.1"
+    
+    
+    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")
+    
+    
+    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")
+    
+    
+    def get_interface_details(self, tenant_id, net_id, port_id):
+        """
+        Retrieves the remote interface that is attached at this
+        particular port.
+        """
+        print("get_interface_details() called\n")
+        #returns the remote interface UUID
+        return "/tenant1/networks/net_id/portid/vif2.0"
+    
+    
+    def get_all_attached_interfaces(self, tenant_id, net_id):
+        """
+        Retrieves all remote interfaces that are attached to
+        a particular Virtual Network.
+        """
+        print("get_all_attached_interfaces() called\n")
+        # returns a list of all attached remote interfaces
+        vifs_on_net = ["/tenant1/networks/net_id/portid/vif2.0", "/tenant1/networks/10/121/vif1.1"]
+        return vifs_on_net
         
\ No newline at end of file