Commit a9d62221 by Fukász Rómeó Ervin

os templates, credentials, compute creation, compute delete

parent 760e8dc0
......@@ -25,7 +25,9 @@ with requests.Session() as session:
print("csrf-token")
print("----------")
print("status_code: " + str(req.status_code))
print(json.loads(req.text)["result"])
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# Bejelentkezes
......@@ -38,13 +40,9 @@ with requests.Session() as session:
print("login")
print("-----")
print("status_code: " + str(req.status_code))
if req.status_code == 200:
print(json.loads(req.text)["result"])
else:
print(json.loads(req.text)["result"])
errors = json.loads(req.text)["errors"]
for error in errors:
print(error)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# query interface
......@@ -52,7 +50,9 @@ with requests.Session() as session:
print("query-interface")
print("---------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# osszes vm collectionkent
......@@ -61,17 +61,21 @@ with requests.Session() as session:
print("compute-collection")
print("------------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# az elso vm a listabol
vmid = json.loads(req.text)["resources"][0]["id"]
req = session.get(server + "occi/compute/" + str(vmid) + "/",
req = session.get(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False)
print("compute-"+str(vmid))
print("------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# ha nem active, akkor azza tesszuk
......@@ -82,13 +86,15 @@ with requests.Session() as session:
headers["X-CSRFToken"] = req.cookies['csrftoken']
except:
pass
req = session.post(server + "occi/compute/" + str(vmid) + "/",
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps({"action": action + "start"}))
print("compute-" + str(vmid) + "-start")
print("---------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# restart
......@@ -98,13 +104,15 @@ with requests.Session() as session:
pass
actionatrs = {"method": "cold"}
actioninv = {"action": action + "restart", "attributes": actionatrs}
req = session.post(server + "occi/compute/" + str(vmid) + "/",
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-"+str(vmid) + "-restart")
print("-----------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# suspend
......@@ -114,21 +122,82 @@ with requests.Session() as session:
pass
actioninv["action"] = action + "suspend"
actioninv["attributes"]["method"] = "suspend"
req = session.post(server + "occi/compute/" + str(vmid) + "/",
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-" + str(vmid) + "-suspend")
print("-----------------")
print("status_code: " + str(req.status_code))
print(req.text)
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# nem letezo action
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
actioninv["action"] = action + "noaction"
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-" + str(vmid) + "-noaction")
print("-------------------")
print("status_code: " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# vm krealas
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
# a template mixinje benne kell legyen az adatokban
# az osszes template a query interfacen megjelenik mint mixin
# azok a mixinek templatek amik az os_tpl mixintol fuggnek
putdata = {"mixins": [
"http://circlecloud.org/occi/templates/os#os_template_1"],
"other_occi_compute_data": "may be provided"}
req = session.put(server + "occi/compute/1/",
headers=headers, verify=False,
data=json.dumps(putdata))
print("create_compute")
print("--------------")
print("status_code: " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# vm torles
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
vmid = json.loads(req.text)["id"]
req = session.delete(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(putdata))
print("delete_compute")
print("--------------")
print("status_code: " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
print
# Kijelentkezes
req = session.get(server + "occi/logout/", headers=headers,
verify=False)
print("logout")
print("------")
print("status_code: " + str(req.status_code))
print(json.loads(req.text)["result"])
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
except ConnectionError as e:
print(e)
......@@ -38,24 +38,43 @@ class Compute(Resource):
def __init__(self, vm):
""" Creates a Compute instance of a VM instance object """
super(Compute, self).__init__(
"http://schemas.ogf.org/occi/infrastructure#compute", vm.pk)
"http://schemas.ogf.org/occi/infrastructure#compute",
str(vm.pk))
self.vm = vm
self.attributes = self.set_attributes()
self.actions = action_list_for_resource(COMPUTE_ACTIONS)
self.mixins = [
"http://circlecloud.org/occi/infrastructure#credentials",
]
if vm.template:
self.mixins.append(
"http://circlecloud.org/occi/templates/os#os_template_" +
str(vm.template.pk))
def set_attributes(self):
""" Sets the attributes of the Compute object based on the VM
instance. """
attributes = {}
attributes["occi.compute.architecture"] = (COMPUTE_ARCHITECTURES
.get(self.vm.arch))
attributes["occi.compute.architecture"] = (
COMPUTE_ARCHITECTURES.get(self.vm.arch))
attributes["occi.compute.cores"] = self.vm.num_cores
attributes["occi.compute.hostname"] = self.vm.short_hostname
attributes["occi.compute.share"] = self.vm.priority
attributes["occi.compute.memory"] = self.vm.ram_size / 1024.0
attributes["occi.compute.state"] = COMPUTE_STATES.get(self.vm.state)
attributes["occi.compute.state.message"] = (COMPUTE_STATE_MESSAGES
.get(self.vm.state))
attributes["occi.compute.state.message"] = (
COMPUTE_STATE_MESSAGES.get(self.vm.state))
attributes["org.circlecloud.occi.credentials.protocol"] = (
self.vm.access_method)
attributes["org.circlecloud.occi.credentials.host"] = (
self.vm.get_connect_host())
attributes["org.circlecloud.occi.credentials.port"] = (
self.vm.get_connect_port())
attributes["org.circlecloud.occi.credentials.username"] = "cloud"
attributes["org.circlecloud.occi.credentials.password"] = (
self.vm.pw)
attributes["org.circlecloud.occi.credentials.command"] = (
self.vm.get_connect_command())
return attributes
def invoke_action(self, user, action, attributes):
......@@ -140,8 +159,3 @@ class Compute(Resource):
# TODO: save template
raise OcciActionInvocationError(
message="Save action not implemented")
class Network(Resource):
# TODO: network
pass
""" Required instances of the OCCI classes """
from vm.models.instance import InstanceTemplate
from occi_core import Kind, Mixin, Attribute, Action
......@@ -103,7 +104,6 @@ IPNETWORK_ATTRIBUTES = [
IPNETWORK_MIXIN = Mixin("http://schemas.ogf.org/occi/infrastructure/network#",
"ipnetwork", title="IP Network Mixin",
location="/network/ipnetwork",
applies=("http://shemas.ogf.org/occi/infrastructure" +
"#network"))
......@@ -166,8 +166,6 @@ IPNETWORKINTERFACE_MIXIN = Mixin("http://schemas.ogf.org/occi/" +
"infrastructure/networkinterface#",
"ipnetworkinterface",
title="IP Network Interface Mixin",
location="/networkinterface" +
"/ipnetworkinterface/",
applies="http://schemas.ogf.org/occi/" +
"infrastructure#networkinterface")
......@@ -191,18 +189,81 @@ STORAGELINK_KIND = Kind("http://schemas.ogf.org/occi/infrastructure#",
parent="http://schemas.ogf.org/occi/core#link",
atrributes=STORAGELINK_ATTRIBUTES)
# TODO: OS Templates and Credentials
ACTION_ARRAYS = [COMPUTE_ACTIONS, NETWORK_ACTIONS, STORAGE_ACTIONS]
CREDENTIALS_ATTRIBUTES = [
Attribute("org.circlecloud.occi.credentials.protocol", "String", False,
False, description="The protocol to be used to access the "
"compute instance."),
Attribute("org.circlecloud.occi.credentials.host", "String", False,
False, description="The host to be used to access the compute " +
"instance."),
Attribute("org.circlecloud.occi.credentials.port", "Integer", False,
False, description="The port to be used to access the compute " +
"instance."),
Attribute("org.circlecloud.occi.credentials.username", "String", False,
False, description="The username to be used to access the " +
"compute instance."),
Attribute("org.circlecloud.occi.credentials.password", "String", False,
False, description="The password to be used to acces the " +
"compute instance."),
Attribute("org.circlecloud.occi.credentials.command", "String", False,
False, description="The full command that may be used to " +
"connect to the compute instance."),
]
CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure#",
"credentials",
title="Credentials Mixin",
attributes=CREDENTIALS_ATTRIBUTES,
applies="http://schemas.ogf.org/occi/infrastructure" +
"#compute")
OS_TPL_MIXIN = Mixin("http://schemas.ogf.org/occi/infrastructure#",
"os_tpl",
title="OS Template")
ACTION_ARRAYS = [
COMPUTE_ACTIONS,
# NETWORK_ACTIONS,
# STORAGE_ACTIONS,
]
def ALL_KINDS():
return [ENTITY_KIND, RESOURCE_KIND, LINK_KIND, COMPUTE_KIND, NETWORK_KIND,
STORAGE_KIND, NETWORKINTERFACE_KIND]
return [
ENTITY_KIND,
RESOURCE_KIND,
LINK_KIND,
COMPUTE_KIND,
# NETWORK_KIND,
# STORAGE_KIND,
# NETWORKINTERFACE_KIND
]
def os_tpl_mixins(user):
""" Returns an array of all the templates the user has access to. """
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)))
return result
def ALL_MIXINS():
return [IPNETWORK_MIXIN, IPNETWORKINTERFACE_MIXIN]
def ALL_MIXINS(user):
mixins = [
# IPNETWORK_MIXIN,
# IPNETWORKINTERFACE_MIXIN,
CREDENTIALS_MIXIN,
OS_TPL_MIXIN,
]
template_mixins = os_tpl_mixins(user)
for template in template_mixins:
mixins.append(template)
return mixins
def ALL_ACTIONS():
......
"""" Utilities for the OCCI implementation of CIRCLE """
from django.http import JsonResponse, HttpResponse
from django.http import HttpResponse
import json
class OcciResourceInstanceNotExist(Exception):
def __init__(self):
message = "The resource instance does not exist."
super(OcciResourceInstanceNotExist, self).__init__(message)
self.response = JsonResponse({"error": message}, status=404,
charset="utf-8")
class OcciException(Exception):
""" The superclass for OCCI exceptions. It creates a response to be
returned when an error occures. """
def __init__(self, *args, **kwargs):
message = kwargs.get("message", "An error occured.")
status = kwargs.get("status", 400)
super(OcciException, self).__init__(message)
self.response = occi_response({"error": message}, status=status)
class OcciActionInvocationError(Exception):
class OcciResourceInstanceNotExist(OcciException):
""" An exception to be raised when a resource instance which has been
asked for does not exist. """
def __init__(self, *args, **kwargs):
message = kwargs.get("message", "Could not invoke action.")
super(OcciActionInvocationError, self).__init__(message)
self.response = JsonResponse({"error": message}, status=400,
charset="utf-8")
if "message" not in kwargs:
kwargs["message"] = "The resource instance does not exist."
super(OcciResourceInstanceNotExist, self).__init__(self, **kwargs)
class OcciActionInvocationError(OcciException):
""" An exception to be raised when an action could not be invoked on
an entity instance for some reason """
def __init__(self, *args, **kwargs):
if "message" not in kwargs:
kwargs["message"] = "Could not invoke action."
super(OcciActionInvocationError, self).__init__(self, **kwargs)
class OcciResourceCreationError(OcciException):
""" An exception to be raised when a resource instance could not be
created for a reason. """
def __init__(self, *args, **kwargs):
if "message" not in kwargs:
kwargs["message"] = "Could not create resource instance."
super(OcciResourceCreationError, self).__init__(self, **kwargs)
class OcciResourceDeletionError(OcciException):
""" An exception to be raised when a resource instance could not be
deleted for some reason. """
def __init__(self, *args, **kwargs):
if "message" not in kwargs:
kwargs["message"] = "Could not delete resource instance."
super(OcciResourceDeletionError, self).__init__(self, **kwargs)
class OcciResponse(HttpResponse):
""" A response class with its occi headers set """
# TODO: setting occi specific headers
def init(self, data, response_type, *args, **kwargs):
if response_type == "json":
class OcciRequestNotValid(OcciException):
""" An exception to be raised when the request sent by the client is
not valid for a reason. (e.g, wrong content type, etc.) """
def __init__(self, *args, **kwargs):
if "message" not in kwargs:
kwargs["message"] = "The request is not valid."
super(OcciRequestNotValid, self).__init__(self, **kwargs)
def occi_response(data, *args, **kwargs):
""" This function returns a response with its headers set, like occi
server. The content_type of the response is application/json
by default. """
status = kwargs.get("status", 200)
# TODO: support for renderings other than json (e.g., text/plain)
data = json.dumps(data)
super(OcciResponse, self).__init__(data, status=418)
if response_type == "json":
self["Content-Type"] = "application/json"
else:
self["Content-Type"] = "text/plain"
self["Server"] = "OCCI/1.2"
response = HttpResponse(data, charset="utf-8", status=status,
content_type="application/json; charset=utf-8")
# 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 set_optional_attributes(self, optional_attributes, kwargs):
......@@ -43,15 +114,6 @@ def set_optional_attributes(self, optional_attributes, kwargs):
setattr(self, k, v)
def serialize_attributes(attributes):
""" Creates a list of attributes, that are serializable to json from
a list of Attribute class objects. """
atrs = []
for attribute in attributes:
atrs.append(attribute.render_as_json())
return atrs
def action_list_for_resource(actions):
""" Creates a list of actions for Resource object rendering """
acts = []
......
......@@ -5,15 +5,20 @@
import json
from django.views.generic import View
from django.contrib.auth import logout
from django.http import HttpResponse, JsonResponse, Http404
from django.http import Http404
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import ensure_csrf_cookie
from django.utils.decorators import method_decorator
from vm.models.instance import Instance
from vm.models.instance import Instance, InstanceTemplate
from forms import OcciAuthForm
from occi_infrastructure import Compute
from occi_utils import (OcciResourceInstanceNotExist,
OcciActionInvocationError)
OcciActionInvocationError,
OcciRequestNotValid,
OcciResourceCreationError,
OcciResourceDeletionError,
occi_response,
validate_request)
from occi_instances import ALL_KINDS, ALL_MIXINS, ALL_ACTIONS
......@@ -27,7 +32,7 @@ class OcciLoginView(View):
""" Returns a response with a cookie to be used for requests other
than get. """
result = {"result": "OK"}
return JsonResponse(result, charset="utf-8")
return occi_response(result)
def post(self, request, *args, **kwargs):
""" Returns a response with a cookie to be used for the OCCI api
......@@ -36,12 +41,12 @@ class OcciLoginView(View):
form = OcciAuthForm(data=data, request=request)
if form.is_valid():
result = {"result": "OK"}
return JsonResponse(result, charset="utf-8")
return occi_response(result)
else:
errors = dict([(k, [unicode(e) for e in v])
for k, v in form.errors.items()])
result = {"result": "ERROR", "errors": errors["__all__"]}
return JsonResponse(result, status=400, charset="utf-8")
return occi_response(result, status=400)
class OcciLogoutView(View):
......@@ -49,42 +54,70 @@ class OcciLogoutView(View):
def get(self, request, *args, **kwargs):
logout(request)
result = {"result": "OK"}
return JsonResponse(result, charset="utf-8")
return occi_response(result)
class OcciQueryInterfaceView(View):
""" The view of the OCCI query interface """
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
result = {"kinds": [], "mixins": [], "actions": []}
for kind in ALL_KINDS():
result["kinds"].append(kind.render_as_json())
for mixin in ALL_MIXINS():
for mixin in ALL_MIXINS(request.user):
result["mixins"].append(mixin.render_as_json())
for action in ALL_ACTIONS():
result["actions"].append(action.render_as_json())
return JsonResponse(result, charset="utf-8")
return occi_response(result)
def post(self, request, *args, **kwargs):
return occi_response({"error": "User defined mixins are not " +
"supported."}, status=405)
def delete(self, request, *args, **kwargs):
return occi_response({"error": "User defined mixins are not " +
"supported."}, status=405)
def put(self, request, *args, **kwargs):
return occi_response({"error": "Put method is not defined on the " +
"query interface."}, status=400)
class OcciComputeCollectionView(View):
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponse(status=403)
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
vms = (Instance.get_objects_with_level("user", request.user)
.filter(destroyed_at=None))
json = {"resources": []}
for vm in vms:
json["resources"].append(Compute(vm).render_as_json())
return JsonResponse(json, charset="utf-8")
return occi_response(json)
def put(self, request, *args, **kwargs):
# TODO: vm creation
return occi_response({"message": "TODO"})
try:
Instance.create_from_template(
InstanceTemplate.objects.get(pk=1), request.user)
except Exception:
return occi_response({"test": "tset"})
return occi_response({})
class OcciComputeView(View):
""" View of a compute instance """
def get_vm_object(self, request, vmid):
def get_vm_object(self, user, vmid):
try:
vm = get_object_or_404(Instance.get_objects_with_level("user",
request.user), pk=vmid)
user).filter(destroyed_at=None), pk=vmid)
except Http404:
raise OcciResourceInstanceNotExist()
return Compute(vm)
......@@ -92,20 +125,22 @@ class OcciComputeView(View):
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponse(status=403)
return occi_response({"error": "Authentication required."},
status=403)
try:
compute = self.get_vm_object(request, kwargs["id"])
compute = self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.json_response
return JsonResponse(compute.render_as_json(), charset="utf-8")
return e.response
return occi_response(compute.render_as_json(), charset="utf-8")
def post(self, request, *args, **kwargs):
requestData = json.loads(request.body.decode("utf-8"))
# TODO post request w/o action, compute creation
if not requestData["action"]:
return HttpResponse(status=404)
return occi_response({"error": "Action invocation rendering " +
"is not supplied."},
status=400)
try:
compute = self.get_vm_object(request, kwargs["id"])
compute = self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
......@@ -114,5 +149,49 @@ class OcciComputeView(View):
requestData.get("attributes", None))
except OcciActionInvocationError as e:
return e.response
# TODO: proper return value
return JsonResponse(compute.render_as_json(), status=200)
return occi_response(compute.render_as_json(), status=200)
def put(self, request, *args, **kwargs):
# checking if the requested resource exists
try:
self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist:
# there has to be a mixins array in the provided rendering
data_keys = ["mixins"]
try:
requestData = validate_request(request, True, True,
data_keys=data_keys)
except OcciRequestNotValid as e:
return e.response
ostpl = "http://circlecloud.org/occi/templates/os#os_template_"
for mixin in requestData["mixins"]:
if ostpl in mixin:
tpl_id = int(mixin.replace(ostpl, ""))
try:
template = get_object_or_404(
InstanceTemplate.get_objects_with_level(
"user", request.user), pk=tpl_id)
except Http404:
return occi_response({"error": "Template does not" +
"exist."})
try:
vm = Instance.create_from_template(template,
request.user)
except:
return OcciResourceCreationError().response
compute = Compute(vm)
return occi_response(compute.render_as_json())
# TODO: update compute instance
return occi_response({"error": "Update of compute instances is " +
"not implemented."}, status=501)
def delete(self, request, *args, **kwargs):
try:
compute = self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
compute.vm.destroy(user=request.user)
except:
return OcciResourceDeletionError().response
return occi_response({"result": "Compute instance deleted."})
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