/
release_resources.py
126 lines (100 loc) · 4.75 KB
/
release_resources.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# pylint: disable=unused-argument, no-self-use
from __future__ import absolute_import
from six.moves import http_client
from future.builtins import str
from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist
from swaggapi.api.builder.server.response import Response
from swaggapi.api.builder.server.exceptions import BadRequest
from swaggapi.api.builder.server.request import DjangoRequestView
from rotest.management.models import ResourceData
from rotest.management.common.utils import get_username
from rotest.common.django_utils.common import get_sub_model
from rotest.api.common.models import ReleaseResourcesParamsModel
from rotest.api.test_control.middleware import session_middleware
from rotest.api.common.responses import (FailureResponseModel,
SuccessResponse)
from rotest.management.common.errors import (ResourceAlreadyAvailableError,
ResourceDoesNotExistError,
ResourcePermissionError,
ResourceReleaseError,
ServerError)
class ReleaseResources(DjangoRequestView):
"""Release the given resources one by one.
For complex resource, marks also its sub-resources as free.
Raises:
ResourceReleaseError: if resource is a complex resource and fails.
ResourcePermissionError: if resource is locked by other user.
ResourceAlreadyAvailableError: if resource was already available.
"""
URI = "resources/release_resources"
DEFAULT_MODEL = ReleaseResourcesParamsModel
DEFAULT_RESPONSES = {
http_client.NO_CONTENT: SuccessResponse,
http_client.BAD_REQUEST: FailureResponseModel
}
TAGS = {
"post": ["Resources"]
}
@classmethod
def release_resource(cls, resource, username):
"""Mark the resource as free.
For complex resource, marks also its sub-resources as free.
Args:
resource (ResourceData): resource to release.
username (str): name of the releasing user.
Raises:
ResourceReleaseError: if resource is a complex resource and fails.
ResourcePermissionError: if resource is locked by other user.
ResourceAlreadyAvailableError: if resource was already available.
"""
errors = {}
for sub_resource in resource.get_sub_resources():
try:
cls.release_resource(sub_resource, username)
except ServerError as ex:
errors[sub_resource.name] = (ex.ERROR_CODE, str(ex))
if resource.OWNABLE:
if username is not None and resource.is_available(username):
raise ResourceAlreadyAvailableError("Failed releasing resource"
" %r, it was not locked"
% resource.name)
if username is not None and resource.owner != username:
raise ResourcePermissionError("Failed releasing resource %r, "
"it is locked by %r" %
(resource.name, resource.owner))
resource.owner = ""
resource.owner_time = None
resource.save()
if len(errors) != 0:
raise ResourceReleaseError(errors)
@session_middleware
def post(self, request, sessions, *args, **kwargs):
"""Release the given resources one by one."""
try:
session = sessions[request.model.token]
except KeyError:
raise BadRequest("Invalid token provided!")
errors = {}
username = get_username(request)
with transaction.atomic():
for name in request.model.resources:
try:
resource_data = ResourceData.objects.select_for_update() \
.get(name=name)
except ObjectDoesNotExist:
errors[name] = (ResourceDoesNotExistError.ERROR_CODE,
"Resource %r doesn't exist" % name)
continue
resource = get_sub_model(resource_data)
try:
self.release_resource(resource, username)
session.resources.remove(resource)
except ServerError as ex:
errors[name] = (ex.ERROR_CODE, ex.get_error_content())
if len(errors) > 0:
return Response({
"errors": errors,
"details": "errors occurred while releasing resource"
}, status=http_client.BAD_REQUEST)
return Response({}, status=http_client.NO_CONTENT)