Adding serialization/deserilization for network resources.

Adding fake plugin
This commit is contained in:
Salvatore Orlando 2011-05-27 17:52:06 +01:00
parent 55599d7184
commit e8c29b8b96
6 changed files with 249 additions and 162 deletions

View File

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

View File

@ -13,22 +13,18 @@
# 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 ""

View 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")

View 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

View File

@ -1,3 +1,3 @@
[PLUGIN]
# Quantum plugin provider module
provider = quantum.plugins.SamplePlugin.DummyDataPlugin
provider = quantum.plugins.SamplePlugin.FakePlugin

View 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