Adding serialization/deserilization for network resources.
Adding fake plugin
This commit is contained in:
parent
55599d7184
commit
e8c29b8b96
12
bin/quantum
12
bin/quantum
@ -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)
|
||||
|
||||
|
@ -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 ""
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,3 @@
|
||||
[PLUGIN]
|
||||
# Quantum plugin provider module
|
||||
provider = quantum.plugins.SamplePlugin.DummyDataPlugin
|
||||
provider = quantum.plugins.SamplePlugin.FakePlugin
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user