Commit 89f43b4b by Fukász Rómeó Ervin

refactor occi api views and classes

parent 5a4b3e24
Pipeline #615 failed with stage
in 0 seconds
......@@ -19,10 +19,10 @@
""" Implementation of the OCCI - Core model classes """
from occi_utils import set_optional_attributes
from occi.utils import set_optional_attributes
class Attribute:
class Attribute(object):
""" OCCI 1.2 - CORE - Classification - Attribute """
TYPES = ("Object", "List", "Hash")
......@@ -36,16 +36,16 @@ class Attribute:
self.required = required
set_optional_attributes(self, self.optional_attributes, kwargs)
def render_as_json(self):
json = {"mutable": self.mutable, "required": self.required,
"type": self.type}
def as_dict(self):
res = {"mutable": self.mutable, "required": self.required,
"type": self.type}
if hasattr(self, "pattern"):
json["pattern"] = self.pattern
res["pattern"] = self.pattern
if hasattr(self, "default"):
json["default"] = self.default
res["default"] = self.default
if hasattr(self, "description"):
json["description"] = self.description
return json
res["description"] = self.description
return res
class Category(object):
......@@ -71,24 +71,23 @@ class Kind(Category):
set_optional_attributes(self, self.kind_optional_attributes,
kwargs)
def render_as_json(self):
json = {"term": self.term, "scheme": self.scheme}
def as_dict(self):
res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"):
json["title"] = self.title
res["title"] = self.title
if hasattr(self, "parent"):
json["parent"] = self.parent
res["parent"] = self.parent
if hasattr(self, "location"):
json["location"] = self.location
res["location"] = self.location
if hasattr(self, "attributes"):
json["attributes"] = {}
res["attributes"] = {}
for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute
.render_as_json())
res["attributes"][attribute.name] = (attribute.as_dict())
if hasattr(self, "actions"):
json["actions"] = []
res["actions"] = []
for action in self.actions:
json["actions"].append(action.scheme + action.term)
return json
res["actions"].append(action.scheme + action.term)
return res
class Action(Category):
......@@ -97,16 +96,15 @@ class Action(Category):
def __init(self, *args, **kwargs):
super(Action, self).__init__(*args, **kwargs)
def render_as_json(self):
json = {"term": self.term, "scheme": self.scheme}
def as_dict(self):
res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"):
json["title"] = self.title
res["title"] = self.title
if hasattr(self, "attributes"):
json["attributes"] = {}
res["attributes"] = {}
for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute
.render_as_json())
return json
res["attributes"][attribute.name] = (attribute.as_dict())
return res
class Mixin(Category):
......@@ -120,26 +118,25 @@ class Mixin(Category):
set_optional_attributes(self, self.mixin_optional_attributes,
kwargs)
def render_as_json(self):
json = {"term": self.term, "scheme": self.scheme}
def as_dict(self):
res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"):
json["title"] = self.title
res["title"] = self.title
if hasattr(self, "location"):
json["location"] = self.location
res["location"] = self.location
if hasattr(self, "depends"):
json["depends"] = self.depends
res["depends"] = self.depends
if hasattr(self, "applies"):
json["applies"] = self.applies
res["applies"] = self.applies
if hasattr(self, "attributes"):
json["attributes"] = {}
res["attributes"] = {}
for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute
.render_as_json())
res["attributes"][attribute.name] = (attribute.as_dict())
if hasattr(self, "actions"):
json["actions"] = []
res["actions"] = []
for action in self.actions:
json["actions"].append(action.scheme + action.term)
return json
res["actions"].append(action.scheme + action.term)
return res
class Entity(object):
......@@ -161,24 +158,24 @@ class Resource(Entity):
def __init__(self, *args, **kwargs):
super(Resource, self).__init__(*args, **kwargs)
set_optional_attributes(self, self.resource_optional_attributes,
kwargs)
set_optional_attributes(
self, self.resource_optional_attributes, kwargs)
def render_as_json(self):
json = {"kind": self.kind, "id": self.id}
def as_dict(self):
res = {"kind": self.kind, "id": self.id}
if hasattr(self, "title"):
json["title"] = self.title
res["title"] = self.title
if hasattr(self, "summary"):
json["summary"] = self.summary
res["summary"] = self.summary
if hasattr(self, "attributes"):
json["attributes"] = self.attributes
res["attributes"] = self.attributes
if hasattr(self, "actions"):
json["actions"] = self.actions
res["actions"] = self.actions
if hasattr(self, "links"):
json["links"] = self.links
res["links"] = self.links
if hasattr(self, "mixins"):
json["mixins"] = self.mixins
return json
res["mixins"] = self.mixins
return res
class Link(Entity):
......@@ -190,18 +187,17 @@ class Link(Entity):
super(Link, self).__init__(*args, **kwargs)
self.source = source
self.target = target
set_optional_attributes(self, self.link_optional_attributes,
kwargs)
set_optional_attributes(self, self.link_optional_attributes, kwargs)
def render_as_json(self):
json = {"kind": self.kind, "id": self.id, "source": self.source,
"target": self.target}
def as_dict(self):
res = {"kind": self.kind, "id": self.id, "source": self.source,
"target": self.target}
if hasattr(self, "mixins"):
json["mixins"] = self.mixins
res["mixins"] = self.mixins
if hasattr(self, "attributes"):
json["attributes"] = self.attributes
res["attributes"] = self.attributes
if hasattr(self, "actions"):
json["actions"] = self.actions
res["actions"] = self.actions
if hasattr(self, "title"):
json["title"] = self.title
return json
res["title"] = self.title
return res
......@@ -19,12 +19,13 @@
""" Implementation of the OCCI - Infrastructure extension classes """
from occi_core import Resource, Link
from occi_utils import action_list_for_resource, OcciActionInvocationError
from occi_instances import (COMPUTE_ACTIONS, LEASETIME_ACTIONS,
from occi.core import Resource, Link
from occi.utils import action_list_for_resource, OcciActionInvocationError
from occi.instances import (COMPUTE_ACTIONS, LEASETIME_ACTIONS,
STORAGE_ACTIONS, NETWORK_ACTIONS)
from common.models import HumanReadableException
from celery.exceptions import TimeoutError
from firewall.models import Rule
import logging
......@@ -117,11 +118,11 @@ class Compute(Resource):
for disk in disks:
storages.append(Storage(disk))
for storage in storages:
links.append(StorageLink(self, storage).render_as_json())
links.append(StorageLink(self, storage).as_dict())
nics = [NetworkInterface(self, Network(nic.vlan))
for nic in self.vm.interface_set.all()]
for networkinterface in nics:
links.append(networkinterface.render_as_json())
links.append(networkinterface.as_dict())
return links
def invoke_action(self, user, action, attributes):
......@@ -137,6 +138,10 @@ class Compute(Resource):
self.save(user, attributes)
elif action.endswith("renew"):
self.renew(user)
elif action.endswith("createstorage"):
self.create_disk(user, attributes)
elif action.endswith("downloadstorage"):
self.download_disk(user, attributes)
else:
raise OcciActionInvocationError(message="Undefined action.")
self.__init__(self.vm)
......@@ -147,6 +152,28 @@ class Compute(Resource):
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
def create_disk(self, user, attributes):
if "size" not in attributes:
raise OcciActionInvocationError(
message="Storage size is missing from action attributes!"
)
try:
self.vm.create_disk(user=user, size=attributes["size"],
name=attributes.get("name"))
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
def download_disk(self, user, attributes):
if "url" not in attributes:
raise OcciActionInvocationError(
message="Storage image url is missing from action attributes!"
)
try:
self.vm.download_disk(user=user, url=attributes["url"],
name=attributes.get("name"))
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
def start(self, user):
""" Start action on a compute instance """
try:
......@@ -371,9 +398,50 @@ class NetworkInterface(Link):
self.mixins = [
("http://schemas.ogf.org/occi/infrastructure/networkinterface#" +
"ipnetworkinterface"),
("http://circlecloud.org/occi/infrastructure/networkinterface#" +
"ports"),
]
self.attributes = self.set_attributes()
def invoke_action(self, user, action, attributes):
if action.endswith("addport"):
self.addport(user, attributes)
elif action.endswith("removeport"):
self.removeport(user, attributes)
else:
raise OcciActionInvocationError(message="Undefined action.")
self.__init__(Compute(self.compute.vm), Network(self.network.vlan))
def addport(self, user, attributes):
if "port" not in attributes or "protocol" not in attributes:
raise OcciActionInvocationError(
message="Please supply the protocol and the port!")
try:
self.compute.vm.add_port(user=user, host=self.interface.host,
proto=attributes["protocol"],
port=int(attributes["port"]))
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
except AttributeError:
raise OcciActionInvocationError(
message="Unmanaged interfaces cant add ports."
)
def removeport(self, user, attributes):
if "port" not in attributes or "protocol" not in attributes:
raise OcciActionInvocationError(
message="Please supply the protocol and the port!")
try:
rule = Rule.objects.filter(host=self.interface.host).filter(
dport=attributes["port"]).get(
proto=attributes["protocol"])
except Rule.DoesNotExist:
raise OcciActionInvocationError(message="Port does not exist!")
try:
self.compute.vm.remove_port(user=user, rule=rule)
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
def set_attributes(self):
attributes = {}
attributes["occi.networkinterface.interface"] = (
......@@ -382,10 +450,17 @@ class NetworkInterface(Link):
attributes["occi.networkinterface.state"] = "active"
attributes["occi.networkinterface.state.message"] = (
"The networkinterface is active.")
attributes["occi.networkinterface.address"] = (
unicode(self.interface.host.ipv4))
if self.interface.host:
attributes["occi.networkinterface.address"] = (
unicode(self.interface.host.ipv4))
attributes["occi.networkinterface.gateway"] = (
unicode(self.interface.vlan.network4.ip))
attributes["occi.networkinterface.allocation"] = (
self.network.attributes["occi.network.allocation"])
attributes["org.circlecloud.occi.networkinterface.ports"] = (
self.get_open_ports())
return attributes
def get_open_ports(self):
return [{"port": rule.dport, "protocol": rule.proto}
for rule in Rule.objects.filter(host=self.interface.host)]
......@@ -19,7 +19,7 @@
""" Required instances of the OCCI classes """
from vm.models.instance import InstanceTemplate
from occi_core import Kind, Mixin, Attribute, Action
from occi.core import Kind, Mixin, Attribute, Action
ENTITY_KIND = Kind("http://schemas.ogf.org/occi/core#", "entity",
......@@ -237,6 +237,42 @@ CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure/" +
applies="http://schemas.ogf.org/occi/" +
"infrastructure#compute")
NETWORKINTERFACE_PORTS_ATTRIBUTES = [
Attribute("org.circlecloud.occi.networkinterface.ports", "List", False,
False, description="A list of open ports on the interface."),
]
NETWORKINTERFACE_PORTS_ACTIONS = [
Action(
"http://schemas.ogf.org/occi/infrastructure/networkinterface/action#",
"addport",
title="Open a port on a network interface.",
attributes=[
Attribute("protocol", "Enum {tcp, udp, icmp}", True, False),
Attribute("port", "Integer", False, True),
],
),
Action(
"http://schemas.ogf.org/occi/infrastructure/networkinterface/action#",
"removeport",
title="Closes a port on a network interface.",
attributes=[
Attribute("protocol", "Enum {tcp, udp, icmp}", True, False),
Attribute("port", "Integer", False, True),
],
),
]
NETWORKINTERFACE_PORTS_MIXIN = Mixin(
"http://circlecloud.org/occi/infrastructure/networkinterface#",
"ports",
title="Network interface ports mixin",
attributes=NETWORKINTERFACE_PORTS_ATTRIBUTES,
actions=NETWORKINTERFACE_PORTS_ACTIONS,
applies="http://schemas.ogf.org/occi/infrastructure#networkinterface",
)
LEASETIME_ATTRIBUTES = [
Attribute("org.circlecloud.occi.leasetime.suspend", "String", False,
False, description="The time remaining until the compute " +
......@@ -264,11 +300,13 @@ OS_TPL_MIXIN = Mixin("http://schemas.ogf.org/occi/infrastructure#",
"os_tpl",
title="OS Template")
ACTION_ARRAYS = [
COMPUTE_ACTIONS,
NETWORK_ACTIONS,
STORAGE_ACTIONS,
LEASETIME_ACTIONS,
NETWORKINTERFACE_PORTS_ACTIONS,
]
......@@ -290,10 +328,38 @@ def os_tpl_mixins(user):
templates = InstanceTemplate.get_objects_with_level("user", user)
result = []
for template in templates:
result.append(Mixin("http://circlecloud.org/occi/templates/os#",
"os_template_" + str(template.pk),
title=template.name,
depends=(OS_TPL_MIXIN.scheme + OS_TPL_MIXIN.term)))
template_attrs = [
Attribute("occi.compute.architecture", "Enum {x86, x64}",
True, False,
default={
"x86_64": "x64",
"x86-64 (64 bit)": "x64",
"i686": "x86",
"x86 (32 bit)": "x86"
}[template.arch],
description="CPU Architecture of the instance."),
Attribute("occi.compute.cores", "Integer", True, False,
default=template.num_cores,
description="Number of virtual CPU cores assigned to " +
"the instance."),
Attribute("occi.compute.share", "Integer", True, False,
default=template.priority,
description="Relative number of CPU shares for the " +
"instance."),
Attribute("occi.compute.memory", "Float, 10^9 (GiB)", True, False,
default=template.ram_size,
description="Maximum RAM in gigabytes allocated to " +
"the instance."),
]
result.append(
Mixin(
"http://circlecloud.org/occi/templates/os#",
"os_template_" + str(template.pk),
title=template.name,
depends=(OS_TPL_MIXIN.scheme + OS_TPL_MIXIN.term),
attributes=template_attrs,
)
)
return result
......@@ -304,6 +370,7 @@ def ALL_MIXINS(user):
CREDENTIALS_MIXIN,
OS_TPL_MIXIN,
LEASETIME_MIXIN,
NETWORKINTERFACE_PORTS_MIXIN,
]
template_mixins = os_tpl_mixins(user)
for template in template_mixins:
......
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from occi.utils import OcciRequestNotValid
class EnsureCsrfTokenMixin(object):
@method_decorator(ensure_csrf_cookie)
def dispatch(self, *args, **kwargs):
return super(EnsureCsrfTokenMixin, self).dispatch(*args, **kwargs)
class OcciViewMixin(EnsureCsrfTokenMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return OcciRequestNotValid(message="Authentication required.",
status=403).response
return super(OcciViewMixin, self).dispatch(request, *args, **kwargs)
......@@ -17,13 +17,18 @@
from django.conf.urls import url
from views import (OcciLoginView, OcciLogoutView, OcciQueryInterfaceView,
OcciComputeView, OcciComputeCollectionView,
OcciStorageView, OcciStorageCollectionView,
OcciNetworkView, OcciNetworkCollectionView)
from occi.views import (OcciLoginView, OcciLogoutView, OcciQueryInterfaceView,
OcciComputeView, OcciComputeCollectionView,
OcciStorageView, OcciStorageCollectionView,
OcciNetworkView, OcciNetworkCollectionView,
OcciStoragelinkView, OcciStoragelinkCollectionView,
OcciNetworkInterfaceView,
OcciNetworkInterfaceCollectionView,)
from common.views import GenerateTokenView
urlpatterns = [
url(r'^login/token/$', GenerateTokenView.as_view()),
url(r'^login/$', OcciLoginView.as_view()),
url(r'^logout/$', OcciLogoutView.as_view()),
url(r'^-/$', OcciQueryInterfaceView.as_view()),
......@@ -33,4 +38,11 @@ urlpatterns = [
url(r'^storage/(?P<id>\d+)/$', OcciStorageView.as_view()),
url(r'^network/$', OcciNetworkCollectionView.as_view()),
url(r'^network/(?P<id>\d+)/$', OcciNetworkView.as_view()),
url(r'^storagelink/$', OcciStoragelinkCollectionView.as_view()),
url(r'^storagelink/compute(?P<computeid>\d+)-storage(?P<storageid>\d+)/$',
OcciStoragelinkView.as_view()),
url(r'^networkinterface/$', OcciNetworkInterfaceCollectionView.as_view()),
url(r'^networkinterface/compute(?P<computeid>\d+)-network(?P<networkid>' +
r'\d+)/$',
OcciNetworkInterfaceView.as_view()),
]
......@@ -18,7 +18,7 @@
"""" Utilities for the OCCI implementation of CIRCLE """
from django.http import HttpResponse
from django.http import JsonResponse
import json
......@@ -89,44 +89,33 @@ def occi_response(data, *args, **kwargs):
by default. """
status = kwargs.get("status", 200)
# TODO: support for renderings other than json (e.g., text/plain)
data = json.dumps(data)
response = HttpResponse(data, charset="utf-8", status=status,
content_type="application/json; charset=utf-8")
response = JsonResponse(data, status=status)
# TODO: use Server header instead of OCCI-Server
response["OCCI-Server"] = "OCCI/1.2"
response["Accept"] = "application/json"
return response
def validate_request(request, authentication_required=True,
has_data=False, **kwargs):
""" This function checks if the request's content type is
application/json and if the data is a valid json object. If the
authentication_required parameter is 'True', it will also check if
the user is authenticated. """
# checking if the user is authenticated
if authentication_required:
if not request.user.is_authenticated():
raise OcciRequestNotValid("Authentication required.", status=403)
if has_data:
# checking content type
if request.META.get("CONTENT_TYPE") != "application/json":
raise OcciRequestNotValid("Only application/json content type" +
" is allowed.")
# checking if the data is a valid json
try:
data = json.loads(request.body.decode("utf-8"))
except KeyError:
raise OcciRequestNotValid("The json provided in the request is " +
"not valid.")
# checking if provided keys are in the json
if "data_keys" in kwargs:
for key in kwargs["data_keys"]:
if key not in data:
raise OcciRequestNotValid(key + " key is required.")
# if validation was successful, the function returns the parsed
# json data
return data
def validate_request_data(request, data_keys):
""" This function checks if all the required data keys are set and if the
input is a valid json object. """
# checking content type
if request.META.get("CONTENT_TYPE") != "application/json":
raise OcciRequestNotValid("Only application/json content type" +
" is allowed.")
# checking if the data is a valid json
try:
data = json.loads(request.body.decode("utf-8"))
except KeyError:
raise OcciRequestNotValid("The json provided in the request is " +
"not valid.")
# checking if provided keys are in the json
for key in data_keys:
if key not in data:
raise OcciRequestNotValid(key + " key is required.")
# if validation was successful, the function returns the parsed
# json data
return data
def set_optional_attributes(self, optional_attributes, kwargs):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment