# 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
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 ""
"""
parts = self.path.rsplit('.', 1)
-
+ LOG.debug("Request parts:%s",parts)
if len(parts) > 1:
format = parts[1]
if format in ['json', 'xml']:
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):
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
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)
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]
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]
result.appendChild(node)
else:
# Type is atom
+ LOG.debug("TYPE IS ATOM:%s",data)
node = doc.createTextNode(str(data))
result.appendChild(node)
return result
# 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