Commit 04394fdf by Fukász Rómeó Ervin

occi: implemented network and storage with the links

compute instances can now be modified
minor bug fixes
parent 17a73b8c
Pipeline #443 failed with stage
in 0 seconds
......@@ -358,6 +358,7 @@ LOCAL_APPS = (
'acl',
'monitor',
'request',
'occi',
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login
class OcciAuthForm(AuthenticationForm):
""" An authentication form for the OCCI implementation. """
def __init__(self, request, *args, **kwargs):
super(OcciAuthForm, self).__init__(*args, **kwargs)
self.request = request
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
import requests
import json
# import urllib3
......@@ -16,6 +33,7 @@ loginData = {"username": username, "password": password}
# Csinalunk egy sessiont, hogy a cookie ami az auth-ert felelos
# automatikusan benne maradjon az osszes keresunkben
with requests.Session() as session:
session.timeout = None
headers = {"Content-Type": "application/json", "Referer": server}
# Csrf-Token a bejelentkezeshez
req = session.get(server + "occi/login/", headers=headers,
......@@ -68,7 +86,7 @@ with requests.Session() as session:
vmid = json.loads(req.text)["resources"][0]["id"]
req = session.get(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False)
print("compute-"+str(vmid))
print("compute-" + str(vmid))
print("------------")
print("status_code: " + str(req.status_code))
print
......@@ -100,12 +118,12 @@ with requests.Session() as session:
headers["X-CSRFToken"] = req.cookies['csrftoken']
except:
pass
actionatrs = {"method": "cold"}
actionatrs = {"method": "warm"}
actioninv = {"action": action + "restart", "attributes": actionatrs}
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-"+str(vmid) + "-restart")
print("compute-" + str(vmid) + "-restart")
print("-----------------")
print("status_code: " + str(req.status_code))
print
......@@ -118,12 +136,12 @@ with requests.Session() as session:
headers["X-CSRFToken"] = req.cookies['csrftoken']
except:
pass
actioninv["action"] = action + "suspend"
actioninv["attributes"]["method"] = "suspend"
actioninv["action"] = action + "stop"
actioninv["attributes"]["method"] = "graceful"
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-" + str(vmid) + "-suspend")
print("compute-" + str(vmid) + "-stop")
print("-----------------")
print("status_code: " + str(req.status_code))
print
......@@ -136,11 +154,11 @@ with requests.Session() as session:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
actioninv["action"] = action + "noaction"
actioninv["action"] = action + "renew"
req = session.post(server + "occi/compute/" + vmid + "/",
headers=headers, verify=False,
data=json.dumps(actioninv))
print("compute-" + str(vmid) + "-noaction")
print("compute-" + str(vmid) + "-renew")
print("-------------------")
print("status_code: " + str(req.status_code))
print
......@@ -186,6 +204,50 @@ with requests.Session() as session:
indent=4, separators=(",", ": ")))
print
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
req = session.get(server + "occi/network/1/", headers=headers,
verify=False)
print("storage")
print("-------")
print("status_code " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
req = session.post(server + "occi/network/1/", headers=headers,
verify=False, data=json.dumps({"action": "online"}))
print("storage")
print("-------")
print("status_code " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
try:
headers["X-CSRFToken"] = req.cookies["csrftoken"]
except:
pass
req = session.post(server + "occi/compute/96/", headers=headers,
verify=False, data=json.dumps(
{
"attributes": {
"occi.compute.memory": 0.250
}
}))
print("computerehelelel")
print("-------")
print("status_code " + str(req.status_code))
print
print(json.dumps(json.loads(req.text), sort_keys=True,
indent=4, separators=(",", ": ")))
# Kijelentkezes
req = session.get(server + "occi/logout/", headers=headers,
verify=False)
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
""" Implementation of the OCCI - Core model classes """
......@@ -75,6 +93,7 @@ class Kind(Category):
class Action(Category):
""" OCCI 1.2 - CORE - Classification - Action """
def __init(self, *args, **kwargs):
super(Action, self).__init__(*args, **kwargs)
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
""" Implementation of the OCCI - Infrastructure extension classes """
from occi_core import Resource
from occi_core import Resource, Link
from occi_utils import action_list_for_resource, OcciActionInvocationError
from occi_instances import COMPUTE_ACTIONS
from occi_instances import (COMPUTE_ACTIONS, LEASETIME_ACTIONS,
STORAGE_ACTIONS, NETWORK_ACTIONS)
from common.models import HumanReadableException
from vm.models import InstanceActivity
from celery.exceptions import TimeoutError
import logging
logger = logging.getLogger(__name__)
COMPUTE_STATES = {
......@@ -36,16 +59,21 @@ COMPUTE_ARCHITECTURES = {"x86_64": "x64",
class Compute(Resource):
""" OCCI 1.2 - Infrastructure extension - Compute """
def __init__(self, vm):
""" Creates a Compute instance of a VM instance object """
super(Compute, self).__init__(
"http://schemas.ogf.org/occi/infrastructure#compute",
str(vm.pk))
str(vm.pk),
title=vm.name)
self.vm = vm
self.attributes = self.set_attributes()
self.actions = action_list_for_resource(COMPUTE_ACTIONS)
self.links = self.set_links()
self.actions = action_list_for_resource(
COMPUTE_ACTIONS + LEASETIME_ACTIONS)
self.mixins = [
"http://circlecloud.org/occi/infrastructure#credentials",
"http://circlecloud.org/occi/infrastructure/compute#credentials",
"http://circlecloud.org/occi/infrastructure/compute#leasetime",
]
if vm.template:
self.mixins.append(
......@@ -56,7 +84,6 @@ class Compute(Resource):
""" Sets the attributes of the Compute object based on the VM
instance. """
attributes = {}
attributes["occi.core.title"] = self.vm.name
attributes["occi.compute.architecture"] = (
COMPUTE_ARCHITECTURES.get(self.vm.arch))
attributes["occi.compute.cores"] = self.vm.num_cores
......@@ -77,38 +104,48 @@ class Compute(Resource):
self.vm.pw)
attributes["org.circlecloud.occi.credentials.command"] = (
self.vm.get_connect_command())
attributes["org.circlecloud.occi.leasetime.suspend"] = (
unicode(self.vm.time_of_suspend)[:-16])
attributes["org.circlecloud.occi.leasetime.remove"] = (
unicode(self.vm.time_of_delete)[:-16])
return attributes
def set_links(self):
links = []
disks = self.vm.disks.all()
storages = []
for disk in disks:
storages.append(Storage(disk))
for storage in storages:
links.append(StorageLink(self, storage).render_as_json())
nics = [NetworkInterface(self, Network(nic.vlan))
for nic in self.vm.interface_set.all()]
for networkinterface in nics:
links.append(networkinterface.render_as_json())
return links
def invoke_action(self, user, action, attributes):
base = "http://schemas.ogf.org/occi/infrastructure/compute/action#"
if action == (base + "start"):
if action.endswith("start"):
self.start(user)
elif action == (base + "stop"):
elif action.endswith("stop"):
self.stop(user, attributes)
elif action == (base + "restart"):
elif action.endswith("restart"):
self.restart(user, attributes)
elif action == (base + "suspend"):
elif action.endswith("suspend"):
self.suspend(user, attributes)
elif action == (base + "save"):
elif action.endswith("save"):
self.save(user, attributes)
elif action.endswith("renew"):
self.renew(user)
else:
raise OcciActionInvocationError(message="Undefined action.")
# to refresh Compute attribute
self.__init__(self.vm)
def has_agent(self):
last_boot_time = self.vm.activity_log.filter(
succeeded=True, activity_code__in=(
"vm.Instance.deploy", "vm.Instance.reset",
"vm.Instance.reboot")).latest("finished").finished
def renew(self, user):
try:
InstanceActivity.objects.filter(
activity_code="vm.Instance.agent.starting",
started__gt=last_boot_time, instance=self.vm
).latest("started")
except InstanceActivity.DoesNotExist: # no agent since last boot
return False
return True
self.vm.renew(user=user, force=True)
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
def start(self, user):
""" Start action on a compute instance """
......@@ -125,13 +162,31 @@ class Compute(Resource):
if "method" not in attributes:
raise OcciActionInvocationError(message="No method given.")
if attributes["method"] in ("graceful", "acpioff",):
if not self.has_agent():
raise OcciActionInvocationError(
message="User agent is required.")
# NOTE: at kene nezni hogy minden except ag kell-e
timeout = self.vm.shutdown.remote_timeout + 10
result = None
try:
self.vm.shutdown(task=None, user=user)
task = self.vm.shutdown.async(user=user)
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
logger.exception("Could not start operation")
result = e
except Exception as e:
logger.exception("Could not start operation")
result = e
else:
try:
task.get(timeout=timeout)
except TimeoutError:
logger.debug("Result didn't arrive in %ss",
timeout, exc_info=True)
except HumanReadableException as e:
logger.exception(e)
result = e
except Exception as e:
logger.debug("Operation failed.", exc_info=True)
result = e
if result:
raise OcciActionInvocationError(unicode(result))
elif attributes["method"] in ("poweroff",):
try:
self.vm.shut_off(user=user)
......@@ -146,11 +201,8 @@ class Compute(Resource):
if "method" not in attributes:
raise OcciActionInvocationError(message="No method given.")
if attributes["method"] in ("graceful", "warm",):
if not self.has_agent():
raise OcciActionInvocationError(
message="User agent is required.")
try:
self.vm.reboot(user=user)
self.vm.restart(user=user)
except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text())
elif attributes["method"] in ("cold",):
......@@ -180,3 +232,160 @@ class Compute(Resource):
# TODO: save template
raise OcciActionInvocationError(
message="Save action not implemented")
STORAGE_STATES_BY_IN_USE = {
"online": "The disk is attached to an active compute instance.",
"offline": "The disk is not used by any compute instances at the moment.",
"error": "The disk is destroyed.",
}
class Storage(Resource):
""" OCCI 1.2 - Infrastructure extension - Storage """
def __init__(self, disk):
super(Storage, self).__init__(
"http://schemas.ogf.org/occi/infrastructure#storage",
str(disk.pk))
self.disk = disk
self.actions = action_list_for_resource(STORAGE_ACTIONS)
self.attributes = self.set_attributes()
def set_attributes(self):
attributes = {}
attributes["occi.storage.size"] = float(self.disk.size) / 10**9
if self.disk.destroyed is None:
if self.disk.is_in_use:
state_key = "online"
else:
state_key = "offline"
else:
state_key = "error"
attributes["occi.storage.state"] = state_key
attributes["occi.storage.state.message"] = (
STORAGE_STATES_BY_IN_USE[state_key])
return attributes
def invoke_action(self, user, action, attributes):
message = ("Action invokation on storage instances is not supported. "
"Please invoke actions on compute instances instead.")
raise OcciActionInvocationError(message=message, status=405)
STORAGELINK_STATES_BY_STORAGE_STATE = {
"online": "active",
"offline": "inactive",
"error": "error",
}
class StorageLink(Link):
""" OCCI 1.2 - Infrastructure extension - StorageLink """
def __init__(self, compute, storage):
super(StorageLink, self).__init__(
{
"location": "/compute/" + compute.id,
"kind": "http://schemas.ogf.org/occi/infrastructure#compute",
},
{
"location": "/storage/" + storage.id,
"kind": "http://schemas.ogf.org/occi/infrastructure#storage",
},
"http://schemas.ogf.org/occi/infrastructure#storagelink",
"compute" + compute.id + "-" + "storage" + storage.id
)
self.compute = compute
self.storage = storage
self.attributes = self.set_attributes()
def set_attributes(self):
attributes = {}
attributes["occi.storagelink.deviceid"] = self.storage.disk.pk
attributes["occi.storagelink.mountpoint"] = (
"/dev/%s%s" % (self.storage.disk.device_type,
self.storage.disk.dev_num)
)
attributes["occi.storagelink.state"] = (
STORAGELINK_STATES_BY_STORAGE_STATE[
self.storage.attributes["occi.storage.state"]])
attributes["occi.storagelink.state.message"] = (
self.storage.attributes["occi.storage.state.message"])
return attributes
class Network(Resource):
""" OCCI 1.2 - Infrastructure extension - Network """
def __init__(self, vlan):
super(Network, self).__init__(
"http://schemas.ogf.org/occi/infrastructure#network",
str(vlan.pk),
)
self.vlan = vlan
self.actions = action_list_for_resource(NETWORK_ACTIONS)
self.attributes = self.set_attributes()
self.mixins = [
"http://schemas.ogf.org/occi/infrastructure/network#ipnetwork",
]
def set_attributes(self):
attributes = {}
attributes["occi.network.vlan"] = self.vlan.vid
attributes["occi.network.state"] = "active"
attributes["occi.network.state.message"] = (
"The network instance is active.")
attributes["occi.network.address"] = unicode(self.vlan.network4)
attributes["occi.network.gateway"] = unicode(self.vlan.network4.ip)
attributes["occi.network.allocation"] = (
"static" if self.vlan.dhcp_pool == "" else "dynamic")
return attributes
def invoke_action(self, user, action, attributes):
message = ("Action invokation on network instances is not supported. "
"Please invoke actions on compute instances instead.")
raise OcciActionInvocationError(message=message, status=405)
class NetworkInterface(Link):
""" OCCI 1.2 - Infrastructure extension - NetworkInterace """
def __init__(self, compute, network):
super(NetworkInterface, self).__init__(
{
"location": "/compute/" + compute.id,
"kind": "http://schemas.ogf.org/occi/infrastructure#compute",
},
{
"location": "/network/" + network.id,
"kind": "http://schemas.ogf.org/occi/infrastructure#network",
},
"http://schemas.ogf.org/occi/infrastructure#networkinterface",
"compute" + compute.id + "-" + "network" + network.id
)
self.compute = compute
self.network = network
self.interface = compute.vm.interface_set.get(vlan=network.vlan)
self.mixins = [
("http://schemas.ogf.org/occi/infrastructure/networkinterface#" +
"ipnetworkinterface"),
]
self.attributes = self.set_attributes()
def set_attributes(self):
attributes = {}
attributes["occi.networkinterface.interface"] = (
self.interface.vlan.name)
attributes["occi.networkinterface.mac"] = unicode(self.interface.mac)
attributes["occi.networkinterface.state"] = "active"
attributes["occi.networkinterface.state.message"] = (
"The networkinterface is active.")
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"])
return attributes
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
""" Required instances of the OCCI classes """
from vm.models.instance import InstanceTemplate
......@@ -211,21 +229,46 @@ CREDENTIALS_ATTRIBUTES = [
"connect to the compute instance."),
]
CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure#",
CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure/" +
"compute#",
"credentials",
title="Credentials Mixin",
attributes=CREDENTIALS_ATTRIBUTES,
applies="http://schemas.ogf.org/occi/" +
"infrastructure#compute")
LEASETIME_ATTRIBUTES = [
Attribute("org.circlecloud.occi.leasetime.suspend", "String", False,
False, description="The time remaining until the compute " +
"instance is suspended."),
Attribute("org.circlecloud.occi.leasetime.remove", "String", False,
False, description="The time remaining until the compute " +
"instance is deleted."),
]
LEASETIME_ACTIONS = [
Action("http://circlecloud.org/occi/infrastructure/compute/action#",
"renew", title="Renew the lease time of the compute instance."),
]
LEASETIME_MIXIN = Mixin("http://circlecloud.org/occi/infrastucture/compute#",
"leasetime",
title="Compute Lease Time Mixin",
attributes=LEASETIME_ATTRIBUTES,
actions=LEASETIME_ACTIONS,
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,
NETWORK_ACTIONS,
STORAGE_ACTIONS,
LEASETIME_ACTIONS,
]
......@@ -235,9 +278,10 @@ def ALL_KINDS():
RESOURCE_KIND,
LINK_KIND,
COMPUTE_KIND,
# NETWORK_KIND,
# STORAGE_KIND,
# NETWORKINTERFACE_KIND
NETWORK_KIND,
STORAGE_KIND,
NETWORKINTERFACE_KIND,
STORAGELINK_KIND,
]
......@@ -255,10 +299,11 @@ def os_tpl_mixins(user):
def ALL_MIXINS(user):
mixins = [
# IPNETWORK_MIXIN,
# IPNETWORKINTERFACE_MIXIN,
IPNETWORK_MIXIN,
IPNETWORKINTERFACE_MIXIN,
CREDENTIALS_MIXIN,
OS_TPL_MIXIN,
LEASETIME_MIXIN,
]
template_mixins = os_tpl_mixins(user)
for template in template_mixins:
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
"""" Utilities for the OCCI implementation of CIRCLE """
from django.http import HttpResponse
......@@ -7,6 +25,7 @@ import json
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)
......@@ -17,6 +36,7 @@ class OcciException(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):
if "message" not in kwargs:
kwargs["message"] = "The resource instance does not exist."
......@@ -26,6 +46,7 @@ class OcciResourceInstanceNotExist(OcciException):
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."
......@@ -35,6 +56,7 @@ class OcciActionInvocationError(OcciException):
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."
......@@ -44,6 +66,7 @@ class OcciResourceCreationError(OcciException):
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."
......@@ -53,6 +76,7 @@ class OcciResourceDeletionError(OcciException):
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."
......
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
from views import (OcciLoginView, OcciLogoutView, OcciQueryInterfaceView,
OcciComputeView, OcciComputeCollectionView)
OcciComputeView, OcciComputeCollectionView,
OcciStorageView, OcciStorageCollectionView,
OcciNetworkView, OcciNetworkCollectionView)
urlpatterns = [
......@@ -9,4 +29,8 @@ urlpatterns = [
url(r'^-/$', OcciQueryInterfaceView.as_view()),
url(r'^compute/$', OcciComputeCollectionView.as_view()),
url(r'^compute/(?P<id>\d+)/$', OcciComputeView.as_view()),
url(r'^storage/$', OcciStorageCollectionView.as_view()),
url(r'^storage/(?P<id>\d+)/$', OcciStorageView.as_view()),
url(r'^network/$', OcciNetworkCollectionView.as_view()),
url(r'^network/(?P<id>\d+)/$', OcciNetworkView.as_view()),
]
# Copyright 2017 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
""" The views of the OCCI implementation of CIRCLE.
These views handle the http requests of the API. """
......@@ -10,8 +28,10 @@ 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, InstanceTemplate
from storage.models import Disk
from firewall.models import Vlan
from forms import OcciAuthForm
from occi_infrastructure import Compute
from occi_infrastructure import Compute, Storage, Network
from occi_utils import (OcciResourceInstanceNotExist,
OcciActionInvocationError,
OcciRequestNotValid,
......@@ -20,6 +40,10 @@ from occi_utils import (OcciResourceInstanceNotExist,
occi_response,
validate_request)
from occi_instances import ALL_KINDS, ALL_MIXINS, ALL_ACTIONS
from common.models import HumanReadableException
import logging
log = logging.getLogger(__name__)
class OcciLoginView(View):
......@@ -38,6 +62,8 @@ class OcciLoginView(View):
""" Returns a response with a cookie to be used for the OCCI api
requests. """
data = json.loads(request.body.decode("utf-8"))
log.error(data)
print(data)
form = OcciAuthForm(data=data, request=request)
if form.is_valid():
result = {"result": "OK"}
......@@ -51,6 +77,7 @@ class OcciLoginView(View):
class OcciLogoutView(View):
""" Logout """
def get(self, request, *args, **kwargs):
logout(request)
result = {"result": "OK"}
......@@ -84,7 +111,7 @@ class OcciQueryInterfaceView(View):
def put(self, request, *args, **kwargs):
return occi_response({"error": "Put method is not defined on the " +
"query interface."}, status=400)
"query interface."}, status=400)
class OcciComputeCollectionView(View):
......@@ -94,7 +121,7 @@ class OcciComputeCollectionView(View):
validate_request(request)
except OcciRequestNotValid as e:
return e.response
vms = (Instance.get_objects_with_level("user", request.user)
vms = (Instance.get_objects_with_level("owner", request.user)
.filter(destroyed_at=None))
json = {"resources": []}
for vm in vms:
......@@ -114,10 +141,11 @@ class OcciComputeCollectionView(View):
class OcciComputeView(View):
""" View of a compute instance """
def get_vm_object(self, user, vmid):
try:
vm = get_object_or_404(Instance.get_objects_with_level("user",
user).filter(destroyed_at=None), pk=vmid)
vm = get_object_or_404(Instance.get_objects_with_level(
"owner", user).filter(destroyed_at=None), pk=vmid)
except Http404:
raise OcciResourceInstanceNotExist()
return Compute(vm)
......@@ -135,21 +163,42 @@ class OcciComputeView(View):
def post(self, request, *args, **kwargs):
requestData = json.loads(request.body.decode("utf-8"))
if not requestData["action"]:
return occi_response({"error": "Action invocation rendering " +
"is not supplied."},
status=400)
try:
compute = self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
compute.invoke_action(request.user,
requestData.get("action", None),
requestData.get("attributes", None))
except OcciActionInvocationError as e:
return e.response
return occi_response(compute.render_as_json(), status=200)
if "action" in requestData:
try:
compute = self.get_vm_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
compute.invoke_action(request.user,
requestData.get("action", None),
requestData.get("attributes", None))
except OcciActionInvocationError as e:
return e.response
return occi_response(compute.render_as_json(), status=200)
elif "attributes" in requestData:
attrs = requestData["attributes"]
try:
vm = get_object_or_404(Instance.get_objects_with_level(
"owner", request.user).filter(destroyed_at=None),
pk=kwargs["id"])
except Http404:
return OcciResourceInstanceNotExist().response
num_cores = attrs.get("occi.compute.cores", vm.num_cores)
ram_size = (
attrs.get("occi.compute.memory", vm.ram_size / 1024.0) * 1024)
priority = attrs.get("occi.compute.share", vm.priority)
try:
vm.resources_change(
user=request.user,
num_cores=num_cores,
ram_size=ram_size,
max_ram_size=vm.max_ram_size,
priority=priority
)
except HumanReadableException as e:
log.warning(e.get_user_text())
return occi_response(Compute(vm).render_as_json(), status=200)
return occi_response({"error": "Bad request"}, status=400)
def put(self, request, *args, **kwargs):
# checking if the requested resource exists
......@@ -195,3 +244,135 @@ class OcciComputeView(View):
except:
return OcciResourceDeletionError().response
return occi_response({"result": "Compute instance deleted."})
class OcciStorageCollectionView(View):
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
vms = (Instance.get_objects_with_level("owner", request.user)
.filter(destroyed_at=None))
json = {"resources": []}
for vm in vms:
disks = vm.disks.all()
for disk in disks:
json["resources"].append(Storage(disk).render_as_json())
return occi_response(json)
def put(self, request, *args, **kwargs):
return occi_response({"message": "Not supported."}, status=501)
class OcciStorageView(View):
""" View of a storage instance """
def get_disk_object(self, user, diskid):
try:
disk = get_object_or_404(Disk, pk=diskid)
except Http404:
raise OcciResourceInstanceNotExist()
diskvms = disk.instance_set.all()
uservms = Instance.get_objects_with_level(
"user", user).filter(destroyed_at=None)
if len(diskvms & uservms) > 0:
return Storage(disk)
raise OcciResourceInstanceNotExist()
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
try:
disk = self.get_disk_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
return occi_response(disk.render_as_json(), charset="utf-8")
def post(self, request, *args, **kwargs):
requestData = json.loads(request.body.decode("utf-8"))
if "action" not in requestData:
return occi_response(
{
"error": ("Storage pratial update is not supported. " +
"Action invocation rendering must be supplied."),
},
status=400
)
try:
storage = self.get_disk_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
storage.invoke_action(request.user,
requestData.get("action", None),
requestData.get("attributes", None))
except OcciActionInvocationError as e:
return e.response
return occi_response(storage.render_as_json(), status=200)
class OcciNetworkCollectionView(View):
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
vlans = (Vlan.get_objects_with_level("owner", request.user))
json = {"resources": []}
for vlan in vlans:
json["resources"].append(Network(vlan).render_as_json())
return occi_response(json)
class OcciNetworkView(View):
""" View of a compute instance """
def get_vlan_object(self, user, vlanid):
try:
vlan = get_object_or_404(Vlan.get_objects_with_level(
"user", user), pk=vlanid)
except Http404:
raise OcciResourceInstanceNotExist()
return Network(vlan)
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
try:
validate_request(request)
except OcciRequestNotValid as e:
return e.response
try:
network = self.get_vlan_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
return occi_response(network.render_as_json(), charset="utf-8")
def post(self, request, *args, **kwargs):
requestData = json.loads(request.body.decode("utf-8"))
if "action" not in requestData:
return occi_response(
{
"error": ("Network partial update is not supported. " +
"Action invocation rendering must be supplied."),
},
status=400
)
try:
network = self.get_vlan_object(request.user, kwargs["id"])
except OcciResourceInstanceNotExist as e:
return e.response
try:
network.invoke_action(request.user,
requestData.get("action", None),
requestData.get("attributes", None))
except OcciActionInvocationError as e:
return e.response
return occi_response(network.render_as_json(), status=200)
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