Commit b7650932 authored by Daniele Venzano's avatar Daniele Venzano
Browse files

Finish implementing REST API for roles and quotas

parent 1971d88c
......@@ -216,7 +216,7 @@ class APIEndpoint:
self.sql.user.delete(user_id)
def user_list(self, user: zoe_lib.state.User, **filters) -> List[zoe_lib.state.User]:
"""Generate a optionally filtered list of executions."""
"""Generate a optionally filtered list of users."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
users = self.sql.user.select(**filters)
......@@ -229,7 +229,7 @@ class APIEndpoint:
return self.sql.user.insert(username, fs_uid, role, quota, auth_source)
def user_update(self, user: zoe_lib.state.User, **user_data):
def user_update(self, user: zoe_lib.state.User, user_data):
"""Update a user."""
if 'id' not in user_data:
......@@ -267,6 +267,81 @@ class APIEndpoint:
"""Finds a quota in the database looking it up by its name."""
return self.sql.quota.select(only_one=True, **{'name': quota})
def quota_by_id(self, quota_id) -> zoe_lib.state.Quota:
"""Finds a quota in the database looking it up by its id."""
return self.sql.quota.select(only_one=True, **{'id': quota_id})
def role_by_name(self, role) -> zoe_lib.state.Role:
"""Finds a role in the database looking it up by its name."""
return self.sql.role.select(only_one=True, **{'name': role})
def role_by_id(self, role_id) -> zoe_lib.state.Role:
"""Finds a role in the database looking it up by its id."""
return self.sql.role.select(only_one=True, **{'id': role_id})
def role_new(self, user: zoe_lib.state.User, role_data) -> int:
"""Creates a new role."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
role_id = self.sql.role.insert(role_data['name'])
self.sql.role.update(role_id, **role_data)
return role_id
def role_list(self, user: zoe_lib.state.User, **filters) -> List[zoe_lib.state.Role]:
"""Generate a optionally filtered list of roles."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
users = self.sql.role.select(**filters)
return users
def role_delete(self, user: zoe_lib.state.User, role_id: int):
"""Deletes the role identified by the ID."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
self.sql.role.delete(role_id)
def role_update(self, user: zoe_lib.state.User, role_data):
"""Update a role."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
if 'id' not in role_data:
raise KeyError
self.role_by_id(role_data['id'])
self.sql.role.update(role_data['id'], **role_data)
def quota_new(self, user: zoe_lib.state.User, quota_data) -> int:
"""Creates a new quota."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
role_id = self.sql.quota.insert(quota_data['name'], quota_data['concurrent_executions'], quota_data['memory'], quota_data['cores'])
return role_id
def quota_list(self, user: zoe_lib.state.User, **filters) -> List[zoe_lib.state.Quota]:
"""Generate a optionally filtered list of quotas."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
users = self.sql.quota.select(**filters)
return users
def quota_delete(self, user: zoe_lib.state.User, quota_id: int):
"""Deletes the quota identified by the ID."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
self.sql.quota.delete(quota_id)
def quota_update(self, user: zoe_lib.state.User, quota_data):
"""Update a quota."""
if not user.role.can_change_config:
raise zoe_api.exceptions.ZoeAuthException()
if 'id' not in quota_data:
raise KeyError
self.role_by_id(quota_data['id'])
self.sql.role.update(quota_data['id'], **quota_data)
......@@ -22,6 +22,8 @@ import tornado.web
from zoe_api.rest_api.execution import ExecutionAPI, ExecutionCollectionAPI, ExecutionDeleteAPI, ExecutionEndpointsAPI
from zoe_api.rest_api.info import InfoAPI
from zoe_api.rest_api.user import UserAPI, UserCollectionAPI
from zoe_api.rest_api.role import RoleAPI, RoleCollectionAPI
from zoe_api.rest_api.quota import QuotaAPI, QuotaCollectionAPI
from zoe_api.rest_api.service import ServiceAPI, ServiceLogsAPI
from zoe_api.rest_api.discovery import DiscoveryAPI
from zoe_api.rest_api.statistics import SchedulerStatsAPI
......@@ -47,6 +49,12 @@ def api_init(api_endpoint) -> List[tornado.web.URLSpec]:
tornado.web.url(API_PATH + r'/user/([0-9]+)', UserAPI, route_args),
tornado.web.url(API_PATH + r'/user', UserCollectionAPI, route_args),
tornado.web.url(API_PATH + r'/role/([0-9]+)', RoleAPI, route_args),
tornado.web.url(API_PATH + r'/role', RoleCollectionAPI, route_args),
tornado.web.url(API_PATH + r'/quota/([0-9]+)', QuotaAPI, route_args),
tornado.web.url(API_PATH + r'/quota', QuotaCollectionAPI, route_args),
tornado.web.url(API_PATH + r'/execution/([0-9]+)', ExecutionAPI, route_args),
tornado.web.url(API_PATH + r'/execution/delete/([0-9]+)', ExecutionDeleteAPI, route_args),
tornado.web.url(API_PATH + r'/execution/endpoints/([0-9]+)', ExecutionEndpointsAPI, route_args),
......
# Copyright (c) 2018, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Quota API endpoints."""
import tornado.escape
from zoe_api.rest_api.request_handler import ZoeAPIRequestHandler
from zoe_api.exceptions import ZoeAuthException
class QuotaAPI(ZoeAPIRequestHandler):
"""The Quota API endpoint. Ops on a single quota."""
def get(self, quota_id):
"""HTTP GET method."""
if self.current_user is None:
return
quota = self.api_endpoint.quota_by_id(quota_id)
ret = {
'quota': quota.serialize()
}
self.write(ret)
def post(self, quota_id):
"""HTTP POST method, to edit a quota."""
if self.current_user is None:
return
try:
data = tornado.escape.json_decode(self.request.body)
except ValueError:
self.set_status(400, 'Error decoding JSON data')
return
try:
self.api_endpoint.quota_update(self.current_user, **data)
except KeyError:
self.set_status(400, 'Error decoding JSON data')
return
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(201)
def delete(self, quota_id: int):
"""HTTP DELETE method."""
if self.current_user is None:
return
try:
self.api_endpoint.quota_delete(self.current_user, quota_id)
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(204)
class QuotaCollectionAPI(ZoeAPIRequestHandler):
"""The QuotaCollection API. Ops that interact with the Quota collection."""
def get(self):
"""HTTP GET method"""
if self.current_user is None:
return
filter_dict = {}
filters = [
('name', str)
]
for filter in filters:
if filter[0] in self.request.arguments:
if filter[1] == str:
filter_dict[filter[0]] = self.request.arguments[filter[0]][0].decode('utf-8')
else:
filter_dict[filter[0]] = filter[1](self.request.arguments[filter[0]][0])
try:
quota = self.api_endpoint.quota_list(self.current_user, **filter_dict)
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.write(dict([(r.id, r.serialize()) for r in quota]))
def post(self):
"""HTTP POST method."""
if self.current_user is None:
return
try:
data = tornado.escape.json_decode(self.request.body)
except ValueError:
self.set_status(400, 'Error decoding JSON data')
return
try:
new_id = self.api_endpoint.quota_new(self.current_user, data)
except KeyError:
self.set_status(400, 'Error decoding JSON data')
return
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(201)
self.write({'quota_id': new_id})
# Copyright (c) 2018, Daniele Venzano
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Role API endpoints."""
import tornado.escape
from zoe_api.rest_api.request_handler import ZoeAPIRequestHandler
from zoe_api.exceptions import ZoeAuthException
class RoleAPI(ZoeAPIRequestHandler):
"""The Role API endpoint. Ops on a single role."""
def get(self, role_id):
"""HTTP GET method."""
if self.current_user is None:
return
role = self.api_endpoint.role_by_id(role_id)
ret = {
'role': role.serialize()
}
self.write(ret)
def post(self, role_id):
"""HTTP POST method, to edit a role."""
if self.current_user is None:
return
try:
data = tornado.escape.json_decode(self.request.body)
except ValueError:
self.set_status(400, 'Error decoding JSON data')
return
try:
self.api_endpoint.role_update(self.current_user, **data)
except KeyError:
self.set_status(400, 'Error decoding JSON data')
return
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(201)
def delete(self, role_id: int):
"""HTTP DELETE method."""
if self.current_user is None:
return
try:
self.api_endpoint.role_delete(self.current_user, role_id)
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(204)
class RoleCollectionAPI(ZoeAPIRequestHandler):
"""The RoleCollection API. Ops that interact with the Role collection."""
def get(self):
"""HTTP GET method"""
if self.current_user is None:
return
filter_dict = {}
filters = [
('name', str)
]
for filter in filters:
if filter[0] in self.request.arguments:
if filter[1] == str:
filter_dict[filter[0]] = self.request.arguments[filter[0]][0].decode('utf-8')
else:
filter_dict[filter[0]] = filter[1](self.request.arguments[filter[0]][0])
try:
role = self.api_endpoint.role_list(self.current_user, **filter_dict)
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.write(dict([(r.id, r.serialize()) for r in role]))
def post(self):
"""HTTP POST method."""
if self.current_user is None:
return
try:
data = tornado.escape.json_decode(self.request.body)
except ValueError:
self.set_status(400, 'Error decoding JSON data')
return
try:
new_id = self.api_endpoint.role_new(self.current_user, data)
except KeyError:
self.set_status(400, 'Error decoding JSON data')
return
except ZoeAuthException as e:
self.set_status(401, e.message)
return
self.set_status(201)
self.write({'role_id': new_id})
......@@ -53,7 +53,7 @@ class UserAPI(ZoeAPIRequestHandler):
return
try:
self.api_endpoint.user_update(self.current_user, **data)
self.api_endpoint.user_update(self.current_user, data)
except KeyError:
self.set_status(400, 'Error decoding JSON data')
return
......
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 to comment