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

refactor occi api views and classes

parent 5a4b3e24
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
""" Implementation of the OCCI - Core model classes """ """ 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 """ """ OCCI 1.2 - CORE - Classification - Attribute """
TYPES = ("Object", "List", "Hash") TYPES = ("Object", "List", "Hash")
...@@ -36,16 +36,16 @@ class Attribute: ...@@ -36,16 +36,16 @@ class Attribute:
self.required = required self.required = required
set_optional_attributes(self, self.optional_attributes, kwargs) set_optional_attributes(self, self.optional_attributes, kwargs)
def render_as_json(self): def as_dict(self):
json = {"mutable": self.mutable, "required": self.required, res = {"mutable": self.mutable, "required": self.required,
"type": self.type} "type": self.type}
if hasattr(self, "pattern"): if hasattr(self, "pattern"):
json["pattern"] = self.pattern res["pattern"] = self.pattern
if hasattr(self, "default"): if hasattr(self, "default"):
json["default"] = self.default res["default"] = self.default
if hasattr(self, "description"): if hasattr(self, "description"):
json["description"] = self.description res["description"] = self.description
return json return res
class Category(object): class Category(object):
...@@ -71,24 +71,23 @@ class Kind(Category): ...@@ -71,24 +71,23 @@ class Kind(Category):
set_optional_attributes(self, self.kind_optional_attributes, set_optional_attributes(self, self.kind_optional_attributes,
kwargs) kwargs)
def render_as_json(self): def as_dict(self):
json = {"term": self.term, "scheme": self.scheme} res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"): if hasattr(self, "title"):
json["title"] = self.title res["title"] = self.title
if hasattr(self, "parent"): if hasattr(self, "parent"):
json["parent"] = self.parent res["parent"] = self.parent
if hasattr(self, "location"): if hasattr(self, "location"):
json["location"] = self.location res["location"] = self.location
if hasattr(self, "attributes"): if hasattr(self, "attributes"):
json["attributes"] = {} res["attributes"] = {}
for attribute in self.attributes: for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute res["attributes"][attribute.name] = (attribute.as_dict())
.render_as_json())
if hasattr(self, "actions"): if hasattr(self, "actions"):
json["actions"] = [] res["actions"] = []
for action in self.actions: for action in self.actions:
json["actions"].append(action.scheme + action.term) res["actions"].append(action.scheme + action.term)
return json return res
class Action(Category): class Action(Category):
...@@ -97,16 +96,15 @@ class Action(Category): ...@@ -97,16 +96,15 @@ class Action(Category):
def __init(self, *args, **kwargs): def __init(self, *args, **kwargs):
super(Action, self).__init__(*args, **kwargs) super(Action, self).__init__(*args, **kwargs)
def render_as_json(self): def as_dict(self):
json = {"term": self.term, "scheme": self.scheme} res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"): if hasattr(self, "title"):
json["title"] = self.title res["title"] = self.title
if hasattr(self, "attributes"): if hasattr(self, "attributes"):
json["attributes"] = {} res["attributes"] = {}
for attribute in self.attributes: for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute res["attributes"][attribute.name] = (attribute.as_dict())
.render_as_json()) return res
return json
class Mixin(Category): class Mixin(Category):
...@@ -120,26 +118,25 @@ class Mixin(Category): ...@@ -120,26 +118,25 @@ class Mixin(Category):
set_optional_attributes(self, self.mixin_optional_attributes, set_optional_attributes(self, self.mixin_optional_attributes,
kwargs) kwargs)
def render_as_json(self): def as_dict(self):
json = {"term": self.term, "scheme": self.scheme} res = {"term": self.term, "scheme": self.scheme}
if hasattr(self, "title"): if hasattr(self, "title"):
json["title"] = self.title res["title"] = self.title
if hasattr(self, "location"): if hasattr(self, "location"):
json["location"] = self.location res["location"] = self.location
if hasattr(self, "depends"): if hasattr(self, "depends"):
json["depends"] = self.depends res["depends"] = self.depends
if hasattr(self, "applies"): if hasattr(self, "applies"):
json["applies"] = self.applies res["applies"] = self.applies
if hasattr(self, "attributes"): if hasattr(self, "attributes"):
json["attributes"] = {} res["attributes"] = {}
for attribute in self.attributes: for attribute in self.attributes:
json["attributes"][attribute.name] = (attribute res["attributes"][attribute.name] = (attribute.as_dict())
.render_as_json())
if hasattr(self, "actions"): if hasattr(self, "actions"):
json["actions"] = [] res["actions"] = []
for action in self.actions: for action in self.actions:
json["actions"].append(action.scheme + action.term) res["actions"].append(action.scheme + action.term)
return json return res
class Entity(object): class Entity(object):
...@@ -161,24 +158,24 @@ class Resource(Entity): ...@@ -161,24 +158,24 @@ class Resource(Entity):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Resource, self).__init__(*args, **kwargs) super(Resource, self).__init__(*args, **kwargs)
set_optional_attributes(self, self.resource_optional_attributes, set_optional_attributes(
kwargs) self, self.resource_optional_attributes, kwargs)
def render_as_json(self): def as_dict(self):
json = {"kind": self.kind, "id": self.id} res = {"kind": self.kind, "id": self.id}
if hasattr(self, "title"): if hasattr(self, "title"):
json["title"] = self.title res["title"] = self.title
if hasattr(self, "summary"): if hasattr(self, "summary"):
json["summary"] = self.summary res["summary"] = self.summary
if hasattr(self, "attributes"): if hasattr(self, "attributes"):
json["attributes"] = self.attributes res["attributes"] = self.attributes
if hasattr(self, "actions"): if hasattr(self, "actions"):
json["actions"] = self.actions res["actions"] = self.actions
if hasattr(self, "links"): if hasattr(self, "links"):
json["links"] = self.links res["links"] = self.links
if hasattr(self, "mixins"): if hasattr(self, "mixins"):
json["mixins"] = self.mixins res["mixins"] = self.mixins
return json return res
class Link(Entity): class Link(Entity):
...@@ -190,18 +187,17 @@ class Link(Entity): ...@@ -190,18 +187,17 @@ class Link(Entity):
super(Link, self).__init__(*args, **kwargs) super(Link, self).__init__(*args, **kwargs)
self.source = source self.source = source
self.target = target self.target = target
set_optional_attributes(self, self.link_optional_attributes, set_optional_attributes(self, self.link_optional_attributes, kwargs)
kwargs)
def render_as_json(self): def as_dict(self):
json = {"kind": self.kind, "id": self.id, "source": self.source, res = {"kind": self.kind, "id": self.id, "source": self.source,
"target": self.target} "target": self.target}
if hasattr(self, "mixins"): if hasattr(self, "mixins"):
json["mixins"] = self.mixins res["mixins"] = self.mixins
if hasattr(self, "attributes"): if hasattr(self, "attributes"):
json["attributes"] = self.attributes res["attributes"] = self.attributes
if hasattr(self, "actions"): if hasattr(self, "actions"):
json["actions"] = self.actions res["actions"] = self.actions
if hasattr(self, "title"): if hasattr(self, "title"):
json["title"] = self.title res["title"] = self.title
return json return res
...@@ -19,12 +19,13 @@ ...@@ -19,12 +19,13 @@
""" Implementation of the OCCI - Infrastructure extension classes """ """ Implementation of the OCCI - Infrastructure extension classes """
from occi_core import Resource, Link from occi.core import Resource, Link
from occi_utils import action_list_for_resource, OcciActionInvocationError from occi.utils import action_list_for_resource, OcciActionInvocationError
from occi_instances import (COMPUTE_ACTIONS, LEASETIME_ACTIONS, from occi.instances import (COMPUTE_ACTIONS, LEASETIME_ACTIONS,
STORAGE_ACTIONS, NETWORK_ACTIONS) STORAGE_ACTIONS, NETWORK_ACTIONS)
from common.models import HumanReadableException from common.models import HumanReadableException
from celery.exceptions import TimeoutError from celery.exceptions import TimeoutError
from firewall.models import Rule
import logging import logging
...@@ -117,11 +118,11 @@ class Compute(Resource): ...@@ -117,11 +118,11 @@ class Compute(Resource):
for disk in disks: for disk in disks:
storages.append(Storage(disk)) storages.append(Storage(disk))
for storage in storages: 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)) nics = [NetworkInterface(self, Network(nic.vlan))
for nic in self.vm.interface_set.all()] for nic in self.vm.interface_set.all()]
for networkinterface in nics: for networkinterface in nics:
links.append(networkinterface.render_as_json()) links.append(networkinterface.as_dict())
return links return links
def invoke_action(self, user, action, attributes): def invoke_action(self, user, action, attributes):
...@@ -137,6 +138,10 @@ class Compute(Resource): ...@@ -137,6 +138,10 @@ class Compute(Resource):
self.save(user, attributes) self.save(user, attributes)
elif action.endswith("renew"): elif action.endswith("renew"):
self.renew(user) self.renew(user)
elif action.endswith("createstorage"):
self.create_disk(user, attributes)
elif action.endswith("downloadstorage"):
self.download_disk(user, attributes)
else: else:
raise OcciActionInvocationError(message="Undefined action.") raise OcciActionInvocationError(message="Undefined action.")
self.__init__(self.vm) self.__init__(self.vm)
...@@ -147,6 +152,28 @@ class Compute(Resource): ...@@ -147,6 +152,28 @@ class Compute(Resource):
except HumanReadableException as e: except HumanReadableException as e:
raise OcciActionInvocationError(message=e.get_user_text()) 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): def start(self, user):
""" Start action on a compute instance """ """ Start action on a compute instance """
try: try:
...@@ -371,9 +398,50 @@ class NetworkInterface(Link): ...@@ -371,9 +398,50 @@ class NetworkInterface(Link):
self.mixins = [ self.mixins = [
("http://schemas.ogf.org/occi/infrastructure/networkinterface#" + ("http://schemas.ogf.org/occi/infrastructure/networkinterface#" +
"ipnetworkinterface"), "ipnetworkinterface"),
("http://circlecloud.org/occi/infrastructure/networkinterface#" +
"ports"),
] ]
self.attributes = self.set_attributes() 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): def set_attributes(self):
attributes = {} attributes = {}
attributes["occi.networkinterface.interface"] = ( attributes["occi.networkinterface.interface"] = (
...@@ -382,10 +450,17 @@ class NetworkInterface(Link): ...@@ -382,10 +450,17 @@ class NetworkInterface(Link):
attributes["occi.networkinterface.state"] = "active" attributes["occi.networkinterface.state"] = "active"
attributes["occi.networkinterface.state.message"] = ( attributes["occi.networkinterface.state.message"] = (
"The networkinterface is active.") "The networkinterface is active.")
if self.interface.host:
attributes["occi.networkinterface.address"] = ( attributes["occi.networkinterface.address"] = (
unicode(self.interface.host.ipv4)) unicode(self.interface.host.ipv4))
attributes["occi.networkinterface.gateway"] = ( attributes["occi.networkinterface.gateway"] = (
unicode(self.interface.vlan.network4.ip)) unicode(self.interface.vlan.network4.ip))
attributes["occi.networkinterface.allocation"] = ( attributes["occi.networkinterface.allocation"] = (
self.network.attributes["occi.network.allocation"]) self.network.attributes["occi.network.allocation"])
attributes["org.circlecloud.occi.networkinterface.ports"] = (
self.get_open_ports())
return attributes 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 @@ ...@@ -19,7 +19,7 @@
""" Required instances of the OCCI classes """ """ Required instances of the OCCI classes """
from vm.models.instance import InstanceTemplate 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", ENTITY_KIND = Kind("http://schemas.ogf.org/occi/core#", "entity",
...@@ -237,6 +237,42 @@ CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure/" + ...@@ -237,6 +237,42 @@ CREDENTIALS_MIXIN = Mixin("http://circlecloud.org/occi/infrastructure/" +
applies="http://schemas.ogf.org/occi/" + applies="http://schemas.ogf.org/occi/" +
"infrastructure#compute") "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 = [ LEASETIME_ATTRIBUTES = [
Attribute("org.circlecloud.occi.leasetime.suspend", "String", False, Attribute("org.circlecloud.occi.leasetime.suspend", "String", False,
False, description="The time remaining until the compute " + False, description="The time remaining until the compute " +
...@@ -264,11 +300,13 @@ OS_TPL_MIXIN = Mixin("http://schemas.ogf.org/occi/infrastructure#", ...@@ -264,11 +300,13 @@ OS_TPL_MIXIN = Mixin("http://schemas.ogf.org/occi/infrastructure#",
"os_tpl", "os_tpl",
title="OS Template") title="OS Template")
ACTION_ARRAYS = [ ACTION_ARRAYS = [
COMPUTE_ACTIONS, COMPUTE_ACTIONS,
NETWORK_ACTIONS, NETWORK_ACTIONS,
STORAGE_ACTIONS, STORAGE_ACTIONS,
LEASETIME_ACTIONS, LEASETIME_ACTIONS,
NETWORKINTERFACE_PORTS_ACTIONS,
] ]
...@@ -290,10 +328,38 @@ def os_tpl_mixins(user): ...@@ -290,10 +328,38 @@ def os_tpl_mixins(user):
templates = InstanceTemplate.get_objects_with_level("user", user) templates = InstanceTemplate.get_objects_with_level("user", user)
result = [] result = []
for template in templates: for template in templates:
result.append(Mixin("http://circlecloud.org/occi/templates/os#", 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), "os_template_" + str(template.pk),
title=template.name, title=template.name,
depends=(OS_TPL_MIXIN.scheme + OS_TPL_MIXIN.term))) depends=(OS_TPL_MIXIN.scheme + OS_TPL_MIXIN.term),
attributes=template_attrs,
)
)
return result return result
...@@ -304,6 +370,7 @@ def ALL_MIXINS(user): ...@@ -304,6 +370,7 @@ def ALL_MIXINS(user):
CREDENTIALS_MIXIN, CREDENTIALS_MIXIN,
OS_TPL_MIXIN, OS_TPL_MIXIN,
LEASETIME_MIXIN, LEASETIME_MIXIN,
NETWORKINTERFACE_PORTS_MIXIN,
] ]
template_mixins = os_tpl_mixins(user) template_mixins = os_tpl_mixins(user)
for template in template_mixins: 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 @@ ...@@ -17,13 +17,18 @@
from django.conf.urls import url from django.conf.urls import url
from views import (OcciLoginView, OcciLogoutView, OcciQueryInterfaceView, from occi.views import (OcciLoginView, OcciLogoutView, OcciQueryInterfaceView,
OcciComputeView, OcciComputeCollectionView, OcciComputeView, OcciComputeCollectionView,
OcciStorageView, OcciStorageCollectionView, OcciStorageView, OcciStorageCollectionView,
OcciNetworkView, OcciNetworkCollectionView) OcciNetworkView, OcciNetworkCollectionView,
OcciStoragelinkView, OcciStoragelinkCollectionView,
OcciNetworkInterfaceView,
OcciNetworkInterfaceCollectionView,)
from common.views import GenerateTokenView
urlpatterns = [ urlpatterns = [
url(r'^login/token/$', GenerateTokenView.as_view()),
url(r'^login/$', OcciLoginView.as_view()), url(r'^login/$', OcciLoginView.as_view()),
url(r'^logout/$', OcciLogoutView.as_view()), url(r'^logout/$', OcciLogoutView.as_view()),
url(r'^-/$', OcciQueryInterfaceView.as_view()), url(r'^-/$', OcciQueryInterfaceView.as_view()),
...@@ -33,4 +38,11 @@ urlpatterns = [ ...@@ -33,4 +38,11 @@ urlpatterns = [
url(r'^storage/(?P<id>\d+)/$', OcciStorageView.as_view()), url(r'^storage/(?P<id>\d+)/$', OcciStorageView.as_view()),
url(r'^network/$', OcciNetworkCollectionView.as_view()), url(r'^network/$', OcciNetworkCollectionView.as_view()),
url(r'^network/(?P<id>\d+)/$', OcciNetworkView.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 @@ ...@@ -18,7 +18,7 @@
"""" Utilities for the OCCI implementation of CIRCLE """ """" Utilities for the OCCI implementation of CIRCLE """
from django.http import HttpResponse from django.http import JsonResponse
import json import json
...@@ -89,26 +89,16 @@ def occi_response(data, *args, **kwargs): ...@@ -89,26 +89,16 @@ def occi_response(data, *args, **kwargs):
by default. """ by default. """
status = kwargs.get("status", 200) status = kwargs.get("status", 200)
# TODO: support for renderings other than json (e.g., text/plain) # TODO: support for renderings other than json (e.g., text/plain)
data = json.dumps(data) response = JsonResponse(data, status=status)
response = HttpResponse(data, charset="utf-8", status=status,
content_type="application/json; charset=utf-8")
# TODO: use Server header instead of OCCI-Server # TODO: use Server header instead of OCCI-Server
response["OCCI-Server"] = "OCCI/1.2" response["OCCI-Server"] = "OCCI/1.2"
response["Accept"] = "application/json" response["Accept"] = "application/json"
return response return response
def validate_request(request, authentication_required=True, def validate_request_data(request, data_keys):
has_data=False, **kwargs): """ This function checks if all the required data keys are set and if the
""" This function checks if the request's content type is input is a valid json object. """
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 # checking content type
if request.META.get("CONTENT_TYPE") != "application/json": if request.META.get("CONTENT_TYPE") != "application/json":
raise OcciRequestNotValid("Only application/json content type" + raise OcciRequestNotValid("Only application/json content type" +
...@@ -120,8 +110,7 @@ def validate_request(request, authentication_required=True, ...@@ -120,8 +110,7 @@ def validate_request(request, authentication_required=True,
raise OcciRequestNotValid("The json provided in the request is " + raise OcciRequestNotValid("The json provided in the request is " +
"not valid.") "not valid.")
# checking if provided keys are in the json # checking if provided keys are in the json
if "data_keys" in kwargs: for key in data_keys:
for key in kwargs["data_keys"]:
if key not in data: if key not in data:
raise OcciRequestNotValid(key + " key is required.") raise OcciRequestNotValid(key + " key is required.")
# if validation was successful, the function returns the parsed # if validation was successful, the function returns the parsed
......
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