Commit c977c625 authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Move cloud faults to 'snf_django.lib.api.faults'

Gather all faults from all synnefo projects into common
'snf_django.lib.api.faults'.
parent 12840882
......@@ -43,8 +43,7 @@ from django.utils.translation import ugettext as _
from django.contrib import messages
from astakos.im.models import AstakosUser, Service, Resource
from astakos.im.api.faults import (
Fault, ItemNotFound, InternalServerError, BadRequest)
from snf_django.lib.api import faults
from astakos.im.settings import (
INVITATIONS_ENABLED, COOKIE_NAME, EMAILCHANGE_ENABLED, QUOTAHOLDER_URL,
PROJECTS_VISIBLE)
......@@ -60,7 +59,7 @@ absolute = lambda request, url: request.build_absolute_uri(url)
def render_fault(request, fault):
if isinstance(fault, InternalServerError) and settings.DEBUG:
if isinstance(fault, faults.InternalServerError) and settings.DEBUG:
fault.details = format_exc(fault)
request.serialization = 'text'
......@@ -79,14 +78,14 @@ def api_method(http_method=None):
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise BadRequest('Method not allowed.')
raise faults.BadRequest('Method not allowed.')
response = func(request, *args, **kwargs)
return response
except Fault, fault:
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = InternalServerError('Unexpected error')
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
......@@ -220,12 +219,12 @@ class MenuItem(dict):
def __get_uuid_displayname_catalogs(request, user_call=True):
# Normal Response Codes: 200
# Error Response Codes: badRequest (400)
# Error Response Codes: BadRequest (400)
try:
input_data = json.loads(request.raw_post_data)
except:
raise BadRequest('Request body should be json formatted.')
raise faults.BadRequest('Request body should be json formatted.')
else:
uuids = input_data.get('uuids', [])
if uuids == None and user_call:
......@@ -246,17 +245,17 @@ def __send_feedback(request, email_template_name='im/feedback_mail.txt', user=No
if not user:
auth_token = request.POST.get('auth', '')
if not auth_token:
raise BadRequest('Missing user authentication')
raise faults.BadRequest('Missing user authentication')
try:
user = AstakosUser.objects.get(auth_token=auth_token)
except AstakosUser.DoesNotExist:
raise BadRequest('Invalid user authentication')
raise faults.BadRequest('Invalid user authentication')
form = FeedbackForm(request.POST)
if not form.is_valid():
logger.error("Invalid feedback request: %r", form.errors)
raise BadRequest('Invalid data')
raise faults.BadRequest('Invalid data')
msg = form.cleaned_data['feedback_msg']
data = form.cleaned_data['feedback_data']
......
# Copyright 2011-2012 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
def camelCase(s):
return s[0].lower() + s[1:]
class Fault(Exception):
def __init__(self, message='', details='', name=''):
Exception.__init__(self, message, details, name)
self.message = message
self.details = details
self.name = name or camelCase(self.__class__.__name__)
class BadRequest(Fault):
code = 400
class Unauthorized(Fault):
code = 401
class InternalServerError(Fault):
code = 500
class Forbidden(Fault):
code = 403
class ItemNotFound(Fault):
code = 404
# Copyright 2011-2012 GRNET S.A. All rights reserved.
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -36,13 +36,10 @@ import logging
from functools import wraps
from time import time, mktime
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils import simplejson as json
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
from .faults import (
Fault, Unauthorized, InternalServerError, BadRequest, ItemNotFound)
from snf_django.lib.api import faults
from astakos.im.models import Service
logger = logging.getLogger(__name__)
......@@ -55,31 +52,32 @@ def api_method(http_method=None, token_required=False):
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise BadRequest('Method not allowed.')
raise faults.BadRequest('Method not allowed.')
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
if token_required:
if not x_auth_token:
raise Unauthorized('Access denied')
raise faults.Unauthorized('Access denied')
try:
service = Service.objects.get(auth_token=x_auth_token)
# Check if the token has expired.
if service.auth_token_expires:
if (time() - mktime(service.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
raise faults.Unauthorized('Authentication expired')
except Service.DoesNotExist, e:
raise Unauthorized('Invalid X-Auth-Token')
raise faults.Unauthorized('Invalid X-Auth-Token')
response = func(request, *args, **kwargs)
return response
except Fault, fault:
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = InternalServerError('Unexpected error')
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
@csrf_exempt
@api_method(http_method='POST', token_required=True)
def get_uuid_displayname_catalogs(request):
......@@ -90,6 +88,7 @@ def get_uuid_displayname_catalogs(request):
return __get_uuid_displayname_catalogs(request, user_call=False)
@csrf_exempt
@api_method(http_method='POST', token_required=True)
def send_feedback(request, email_template_name='im/feedback_mail.txt'):
......
# Copyright 2011-2012 GRNET S.A. All rights reserved.
# Copyright 2011-2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
......@@ -40,8 +40,7 @@ from django.http import HttpResponse
from django.utils import simplejson as json
from django.views.decorators.csrf import csrf_exempt
from .faults import (
Fault, Unauthorized, InternalServerError, BadRequest, Forbidden)
from snf_django.lib.api import faults
from . import render_fault, __get_uuid_displayname_catalogs, __send_feedback
from astakos.im.models import AstakosUser
......@@ -64,25 +63,25 @@ def api_method(http_method=None, token_required=False, perms=None):
def wrapper(request, *args, **kwargs):
try:
if http_method and request.method != http_method:
raise BadRequest('Method not allowed.')
raise faults.BadRequest('Method not allowed.')
x_auth_token = request.META.get('HTTP_X_AUTH_TOKEN')
if token_required:
if not x_auth_token:
raise Unauthorized('Access denied')
raise faults.Unauthorized('Access denied')
try:
user = AstakosUser.objects.get(auth_token=x_auth_token)
if not user.has_perms(perms):
raise Forbidden('Unauthorized request')
raise faults.Forbidden('Unauthorized request')
except AstakosUser.DoesNotExist, e:
raise Unauthorized('Invalid X-Auth-Token')
raise faults.Unauthorized('Invalid X-Auth-Token')
kwargs['user'] = user
response = func(request, *args, **kwargs)
return response
except Fault, fault:
except faults.Fault, fault:
return render_fault(request, fault)
except BaseException, e:
logger.exception('Unexpected error: %s' % e)
fault = InternalServerError('Unexpected error')
fault = faults.InternalServerError('Unexpected error')
return render_fault(request, fault)
return wrapper
return decorator
......@@ -95,18 +94,18 @@ def authenticate(request, user=None):
# badRequest (400)
# unauthorised (401)
if not user:
raise BadRequest('No user')
raise faults.BadRequest('No user')
# Check if the is active.
if not user.is_active:
raise Unauthorized('User inactive')
raise faults.Unauthorized('User inactive')
# Check if the token has expired.
if (time() - mktime(user.auth_token_expires.timetuple())) > 0:
raise Unauthorized('Authentication expired')
raise faults.Unauthorized('Authentication expired')
if not user.signed_terms:
raise Unauthorized('Pending approval terms')
raise faults.Unauthorized('Pending approval terms')
response = HttpResponse()
user_info = {
......@@ -135,6 +134,7 @@ def authenticate(request, user=None):
response['Content-Length'] = len(response.content)
return response
@csrf_exempt
@api_method(http_method='POST', token_required=True)
def get_uuid_displayname_catalogs(request, user=None):
......@@ -145,9 +145,11 @@ def get_uuid_displayname_catalogs(request, user=None):
return __get_uuid_displayname_catalogs(request)
@csrf_exempt
@api_method(http_method='POST', token_required=True)
def send_feedback(request, email_template_name='im/feedback_mail.txt', user=None):
def send_feedback(request, email_template_name='im/feedback_mail.txt',
user=None):
# Normal Response Codes: 200
# Error Response Codes: internalServerError (500)
# badRequest (400)
......
......@@ -40,8 +40,7 @@ from django.http import HttpResponse
from django.template.loader import render_to_string
from django.utils import simplejson as json
from synnefo.api.faults import (BadRequest, ServiceUnavailable,
BuildInProgress, OverLimit)
from snf_django.lib.api import faults
from synnefo.api.util import (random_password, get_vm, get_nic_from_index,
get_network_free_address)
from synnefo.db.models import NetworkInterface
......@@ -91,7 +90,7 @@ def change_password(request, vm, args):
# buildInProgress (409),
# overLimit (413)
raise ServiceUnavailable('Changing password is not supported.')
raise faults.NotImplemented('Changing password is not supported.')
@server_action('reboot')
......@@ -109,7 +108,7 @@ def reboot(request, vm, args):
log.info("Reboot VM %s", vm)
reboot_type = args.get('type', '')
if reboot_type not in ('SOFT', 'HARD'):
raise BadRequest('Malformed Request.')
raise faults.BadRequest('Malformed Request.')
backend.reboot_instance(vm, reboot_type.lower())
return HttpResponse(status=202)
......@@ -122,7 +121,7 @@ def start(request, vm, args):
log.info("Start VM %s", vm)
if args:
raise BadRequest('Malformed Request.')
raise faults.BadRequest('Malformed Request.')
backend.startup_instance(vm)
return HttpResponse(status=202)
......@@ -135,7 +134,7 @@ def shutdown(request, vm, args):
log.info("Shutdown VM %s", vm)
if args:
raise BadRequest('Malformed Request.')
raise faults.BadRequest('Malformed Request.')
backend.shutdown_instance(vm)
return HttpResponse(status=202)
......@@ -153,7 +152,7 @@ def rebuild(request, vm, args):
# serverCapacityUnavailable (503),
# overLimit (413)
raise ServiceUnavailable('Rebuild not supported.')
raise faults.NotImplemented('Rebuild not supported.')
@server_action('resize')
......@@ -170,7 +169,7 @@ def resize(request, vm, args):
# overLimit (413),
# resizeNotAllowed (403)
raise ServiceUnavailable('Resize not supported.')
raise faults.NotImplemented('Resize not supported.')
@server_action('confirmResize')
......@@ -187,7 +186,7 @@ def confirm_resize(request, vm, args):
# overLimit (413),
# resizeNotAllowed (403)
raise ServiceUnavailable('Resize not supported.')
raise faults.NotImplemented('Resize not supported.')
@server_action('revertResize')
......@@ -204,7 +203,7 @@ def revert_resize(request, vm, args):
# overLimit (413),
# resizeNotAllowed (403)
raise ServiceUnavailable('Resize not supported.')
raise faults.NotImplemented('Resize not supported.')
@server_action('console')
......@@ -233,11 +232,11 @@ def get_console(request, vm, args):
console_type = args.get('type', '')
if console_type != 'vnc':
raise BadRequest('Type can only be "vnc".')
raise faults.BadRequest('Type can only be "vnc".')
# Use RAPI to get VNC console information for this instance
if get_rsapi_state(vm) != 'ACTIVE':
raise BadRequest('Server not in ACTIVE state.')
raise faults.BadRequest('Server not in ACTIVE state.')
if settings.TEST:
console_data = {'kind': 'vnc', 'host': 'ganeti_node', 'port': 1000}
......@@ -246,7 +245,7 @@ def get_console(request, vm, args):
if console_data['kind'] != 'vnc':
message = 'got console of kind %s, not "vnc"' % console_data['kind']
raise ServiceUnavailable(message)
raise faults.ServiceUnavailable(message)
# Let vncauthproxy decide on the source port.
# The alternative: static allocation, e.g.
......@@ -262,12 +261,12 @@ def get_console(request, vm, args):
fwd = request_vnc_forwarding(sport, daddr, dport, password)
if fwd['status'] != "OK":
raise ServiceUnavailable('vncauthproxy returned error status')
raise faults.ServiceUnavailable('vncauthproxy returned error status')
# Verify that the VNC server settings haven't changed
if not settings.TEST:
if console_data != backend.get_instance_console(vm):
raise ServiceUnavailable('VNC Server settings changed.')
raise faults.ServiceUnavailable('VNC Server settings changed.')
console = {
'type': 'vnc',
......@@ -300,7 +299,7 @@ def set_firewall_profile(request, vm, args):
profile = args.get('profile', '')
log.info("Set VM %s firewall %s", vm, profile)
if profile not in [x[0] for x in NetworkInterface.FIREWALL_PROFILES]:
raise BadRequest("Unsupported firewall profile")
raise faults.BadRequest("Unsupported firewall profile")
backend.set_firewall_profile(vm, profile)
return HttpResponse(status=202)
......@@ -319,11 +318,11 @@ def add(request, net, args):
# overLimit (413)
if net.state != 'ACTIVE':
raise BuildInProgress('Network not active yet')
raise faults.BuildInProgress('Network not active yet')
server_id = args.get('serverRef', None)
if not server_id:
raise BadRequest('Malformed Request.')
raise faults.BadRequest('Malformed Request.')
vm = get_vm(server_id, request.user_uniq, non_suspended=True)
......@@ -333,7 +332,7 @@ def add(request, net, args):
try:
address = get_network_free_address(net)
except EmptyPool:
raise OverLimit('Network is full')
raise faults.OverLimit('Network is full')
log.info("Connecting VM %s to Network %s(%s)", vm, net, address)
......@@ -357,12 +356,12 @@ def remove(request, net, args):
server_id = args.get('attachment', None).split('-')[1]
nic_index = args.get('attachment', None).split('-')[2]
except AttributeError:
raise BadRequest("Malformed Request")
raise faults.BadRequest("Malformed Request")
except IndexError:
raise BadRequest('Malformed Network Interface Id')
raise faults.BadRequest('Malformed Network Interface Id')
if not server_id or not nic_index:
raise BadRequest('Malformed Request.')
raise faults.BadRequest('Malformed Request.')
vm = get_vm(server_id, request.user_uniq, non_suspended=True)
nic = get_nic_from_index(vm, nic_index)
......@@ -370,7 +369,7 @@ def remove(request, net, args):
log.info("Removing NIC %s from VM %s", str(nic.index), vm)
if nic.dirty:
raise BuildInProgress('Machine is busy.')
raise faults.BuildInProgress('Machine is busy.')
else:
vm.nics.all().update(dirty=True)
......
......@@ -31,15 +31,15 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from synnefo.api.faults import BadRequest
from snf_django.lib.api import faults
from synnefo.api.util import api_method
@api_method()
def not_found(request):
raise BadRequest('Not found.')
raise faults.BadRequest('Not found.')
@api_method()
def method_not_allowed(request):
raise BadRequest('Method not allowed')
raise faults.BadRequest('Method not allowed')
......@@ -42,9 +42,9 @@ from django.utils import simplejson as json
from contextlib import contextmanager
from snf_django.lib.api import faults
from synnefo.api import util
from synnefo.api.common import method_not_allowed
from synnefo.api.faults import BadRequest, ItemNotFound, ServiceUnavailable
from synnefo.api.util import api_method, isoformat, isoparse
from synnefo.plankton.backend import ImageBackend
......@@ -170,7 +170,7 @@ def create_image(request):
# backupOrResizeInProgress (409),
# overLimit (413)
raise ServiceUnavailable('Not supported.')
raise faults.NotImplemented('Not supported.')
@api_method('GET')
......@@ -244,7 +244,7 @@ def update_metadata(request, image_id):
metadata = req['metadata']
assert isinstance(metadata, dict)
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
raise faults.BadRequest('Malformed request.')
properties = image['properties']
properties.update(metadata)
......@@ -269,7 +269,7 @@ def get_metadata_item(request, image_id, key):
image = util.get_image(image_id, request.user_uniq)
val = image['properties'].get(key)
if val is None:
raise ItemNotFound('Metadata key not found.')
raise faults.ItemNotFound('Metadata key not found.')
return util.render_meta(request, {key: val}, status=200)
......@@ -293,7 +293,7 @@ def create_metadata_item(request, image_id, key):
assert len(metadict) == 1
assert key in metadict
except (KeyError, AssertionError):
raise BadRequest('Malformed request.')
raise faults.BadRequest('Malformed request.')
val = metadict[key]
image = util.get_image(image_id, request.user_uniq)
......
......@@ -41,11 +41,10 @@ from django.http import HttpResponse
from django.template.loader import render_to_string
from django.utils import simplejson as json
from snf_django.lib.api import faults
from synnefo.api import util
from synnefo.api.actions import network_actions
from synnefo.api.common import method_not_allowed
from synnefo.api.faults import (ServiceUnavailable, BadRequest, Forbidden,
NetworkInUse, OverLimit)
from synnefo import quotas
from synnefo.db.models import Network
from synnefo.db.pools import EmptyPool
......@@ -169,21 +168,21 @@ def create_network(serials, request):
d = req['network']
name = d['name']
except KeyError:
raise BadRequest("Malformed request")
raise faults.BadRequest("Malformed request")
# Get and validate flavor. Flavors are still exposed as 'type' in the
# API.
flavor = d.get("type", None)
if flavor is None:
raise BadRequest("Missing request parameter 'type'")
raise faults.BadRequest("Missing request parameter 'type'")
elif flavor not in Network.FLAVORS.keys():
raise BadRequest("Invalid network type '%s'" % flavor)
raise faults.BadRequest("Invalid network type '%s'" % flavor)
elif flavor not in settings.API_ENABLED_NETWORK_FLAVORS:
raise Forbidden("Can not create network of type '%s'" % flavor)
raise faults.Forbidden("Can not create network of type '%s'" % flavor)
public = d.get("public", False)
if public:
raise Forbidden("Can not create a public network.")
raise faults.Forbidden("Can not create a public network.")
dhcp = d.get('dhcp', True)
......@@ -224,7 +223,7 @@ def create_network(serials, request):
except EmptyPool:
log.error("Failed to allocate resources for network of type: %s",
flavor)
raise ServiceUnavailable("Failed to allocate network resources")
raise faults.ServiceUnavailable("Failed to allocate network resources")
# Create BackendNetwork entries for each Backend
network.create_backend_network()
......@@ -277,11 +276,11 @@ def update_network_name(request, network_id):
try:
name = req['network']['name']
except (TypeError, KeyError):
raise BadRequest('Malformed request.')
raise faults.BadRequest('Malformed request.')
net = util.get_network(network_id, request.user_uniq)
if net.public:
raise Forbidden('Can not rename the public network.')
raise faults.Forbidden('Can not rename the public network.')
if net.deleted:
raise Network.DeletedError
net.name = name
......@@ -303,13 +302,13 @@ def delete_network(request, network_id):
log.info('delete_network %s', network_id)
net = util.get_network(network_id, request.user_uniq, for_update=True)
if net.public:
raise Forbidden('Can not delete the public network.')
raise faults.Forbidden('Can not delete the public network.')
if net.deleted:
raise Network.DeletedError
if net.machines.all(): # Nics attached on network
raise NetworkInUse('Machines are connected to network.')
raise faults.NetworkInUse('Machines are connected to network.')
net.action = 'DESTROY'
net.save()
......@@ -323,11 +322,11 @@ def network_action(request, network_id):
req = util.get_request_dict(request)
log.debug('network_action %s %s', network_id, req)
if len(req) != 1:
raise BadRequest('Malformed request.')