Adding validation of the api body
* cleaning up issues with the load instance(s) * Moving over the validation from creating an instance * making more __name__ and less "strings"
This commit is contained in:
parent
8810955c47
commit
f8602d1e90
@ -33,9 +33,10 @@ keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 use
|
||||
--user $REDDWARF_USER \
|
||||
--role $REDDWARF_ROLE
|
||||
# These are the values
|
||||
REDDWARF_TENANT=reddwarf
|
||||
#REDDWARF_TENANT=reddwarf
|
||||
REDDWARF_TENANT=`keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 tenant-list| grep reddwarf | cut -d ' ' -f 2`
|
||||
echo $REDDWARF_TENANT
|
||||
REDDWARF_USER=$(mysql keystone -e "select id from user where name='reddwarf';" | awk 'NR==2')
|
||||
REDDWARF_USER=`keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 user-list| grep reddwarf | cut -d ' ' -f 2`
|
||||
echo $REDDWARF_USER
|
||||
REDDWARF_TOKEN=$(curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "REDDWARF-PASS"},"tenantName":"reddwarf"}}' -H "Content-type: application/json" http://localhost:35357/v2.0/tokens | python -mjson.tool | grep id | tr -s ' ' | cut -d ' ' -f 3 | sed s/\"/''/g | awk 'NR==2' | cut -d ',' -f 1)
|
||||
echo $REDDWARF_TOKEN
|
||||
|
@ -22,7 +22,7 @@ import webob.exc
|
||||
import wsgi
|
||||
|
||||
|
||||
LOG = logging.getLogger("reddwarf.common.auth")
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthorizationMiddleware(wsgi.Middleware):
|
||||
|
@ -53,3 +53,8 @@ class GuestError(ReddwarfError):
|
||||
|
||||
message = _("An error occurred communicating with the guest: "
|
||||
"%(original_message).")
|
||||
|
||||
|
||||
class BadRequest(openstack_exception.MalformedRequestBody):
|
||||
|
||||
message = _("Required element/key - %(key)s was not specified")
|
||||
|
@ -45,6 +45,7 @@ def load_server(client, uuid):
|
||||
try:
|
||||
server = client.servers.get(uuid)
|
||||
except nova_exceptions.NotFound, e:
|
||||
#TODO(cp16net) would this be the wrong id to show the user?
|
||||
raise rd_exceptions.NotFound(uuid=uuid)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError(str(e))
|
||||
@ -55,6 +56,7 @@ def delete_server(client, server_id):
|
||||
try:
|
||||
client.servers.delete(server_id)
|
||||
except nova_exceptions.NotFound, e:
|
||||
#TODO(cp16net) would this be the wrong id to show the user?
|
||||
raise rd_exceptions.NotFound(uuid=server_id)
|
||||
except nova_exceptions.ClientException, e:
|
||||
raise rd_exceptions.ReddwarfError()
|
||||
@ -173,20 +175,23 @@ class Instance(object):
|
||||
return links
|
||||
|
||||
|
||||
class Instances(Instance):
|
||||
|
||||
def __init__(self, context):
|
||||
#TODO(hub-cap): Fix this, this just cant be right
|
||||
client = create_nova_client(context)
|
||||
self._data_object = client.servers.list()
|
||||
|
||||
def __iter__(self):
|
||||
for item in self._data_object:
|
||||
yield item
|
||||
class Instances(object):
|
||||
|
||||
@staticmethod
|
||||
def load(context):
|
||||
raise Exception("Implement this!")
|
||||
if context is None:
|
||||
raise TypeError("Argument context not defined.")
|
||||
client = create_nova_client(context)
|
||||
servers = client.servers.list()
|
||||
db_infos = DBInstance.find_all()
|
||||
ret = []
|
||||
for db in db_infos:
|
||||
status = InstanceServiceStatus.find_by(instance_id=db.id)
|
||||
for server in servers:
|
||||
if server.id == db.compute_instance_id:
|
||||
ret.append(Instance(context, db, server, status))
|
||||
break
|
||||
return ret
|
||||
|
||||
|
||||
class DatabaseModelBase(ModelBase):
|
||||
@ -220,7 +225,7 @@ class DatabaseModelBase(ModelBase):
|
||||
@classmethod
|
||||
def find_by(cls, **conditions):
|
||||
model = cls.get_by(**conditions)
|
||||
if model == None:
|
||||
if model is None:
|
||||
raise ModelNotFoundError(_("%s Not Found") % cls.__name__)
|
||||
return model
|
||||
|
||||
@ -228,6 +233,10 @@ class DatabaseModelBase(ModelBase):
|
||||
def get_by(cls, **kwargs):
|
||||
return db.db_api.find_by(cls, **cls._process_conditions(kwargs))
|
||||
|
||||
@classmethod
|
||||
def find_all(cls, **kwargs):
|
||||
return db.db_query.find_all(cls, **cls._process_conditions(kwargs))
|
||||
|
||||
@classmethod
|
||||
def _process_conditions(cls, raw_conditions):
|
||||
"""Override in inheritors to format/modify any conditions."""
|
||||
@ -249,10 +258,10 @@ class DBInstance(DatabaseModelBase):
|
||||
self.set_task_status(task_status)
|
||||
|
||||
def _validate(self, errors):
|
||||
if self.task_status is None:
|
||||
errors['task_status'] = "Cannot be none."
|
||||
if InstanceTask.from_code(self.task_id) is None:
|
||||
errors['task_id'] = "Not valid."
|
||||
if self.task_status is None:
|
||||
errors['task_status'] = "Cannot be none."
|
||||
|
||||
def get_task_status(self):
|
||||
return InstanceTask.from_code(self.task_id)
|
||||
|
@ -25,6 +25,7 @@ from reddwarf.common import exception
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.common import wsgi
|
||||
from reddwarf.instance import models, views
|
||||
from reddwarf.common import exception as rd_exceptions
|
||||
|
||||
CONFIG = config.Config
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -39,6 +40,7 @@ class BaseController(wsgi.Controller):
|
||||
],
|
||||
webob.exc.HTTPBadRequest: [
|
||||
models.InvalidModelError,
|
||||
exception.BadRequest,
|
||||
],
|
||||
webob.exc.HTTPNotFound: [
|
||||
exception.NotFound,
|
||||
@ -58,6 +60,23 @@ class BaseController(wsgi.Controller):
|
||||
*self.exclude_attr))
|
||||
|
||||
|
||||
class api_validation:
|
||||
""" api validation wrapper """
|
||||
def __init__(self, action=None):
|
||||
self.action = action
|
||||
|
||||
def __call__(self, f):
|
||||
"""
|
||||
Apply validation of the api body
|
||||
"""
|
||||
def wrapper(*args, **kwargs):
|
||||
body = kwargs['body']
|
||||
if self.action == 'create':
|
||||
InstanceController._validate(body)
|
||||
return f(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class InstanceController(BaseController):
|
||||
"""Controller for instance functionality"""
|
||||
|
||||
@ -65,6 +84,7 @@ class InstanceController(BaseController):
|
||||
"""Return all instances."""
|
||||
LOG.info("req : '%s'\n\n" % req)
|
||||
LOG.info("Detailing a database instance for tenant '%s'" % tenant_id)
|
||||
#TODO(cp16net) return a detailed list instead of index
|
||||
return self.index(req, tenant_id)
|
||||
|
||||
def index(self, req, tenant_id):
|
||||
@ -94,6 +114,7 @@ class InstanceController(BaseController):
|
||||
except exception.ReddwarfError, e:
|
||||
# TODO(hub-cap): come up with a better way than
|
||||
# this to get the message
|
||||
LOG.error(e)
|
||||
return wsgi.Result(str(e), 404)
|
||||
# TODO(cp16net): need to set the return code correctly
|
||||
return wsgi.Result(views.InstanceView(server).data(), 201)
|
||||
@ -107,15 +128,14 @@ class InstanceController(BaseController):
|
||||
context = rd_context.ReddwarfContext(
|
||||
auth_tok=req.headers["X-Auth-Token"],
|
||||
tenant=tenant_id)
|
||||
# TODO(cp16net) : need to handle exceptions here if the delete fails
|
||||
models.Instance.delete(context=context, uuid=id)
|
||||
|
||||
# TODO(cp16net): need to set the return code correctly
|
||||
return wsgi.Result(202)
|
||||
|
||||
@api_validation(action="create")
|
||||
def create(self, req, body, tenant_id):
|
||||
# find the service id (cant be done yet at startup due to
|
||||
# inconsitencies w/ the load app paste
|
||||
# inconsistencies w/ the load app paste
|
||||
# TODO(hub-cap): figure out how to get this to work in __init__ time
|
||||
# TODO(hub-cap): The problem with this in __init__ is that the paste
|
||||
# config is generated w/ the same config file as the db flags that
|
||||
@ -141,6 +161,97 @@ class InstanceController(BaseController):
|
||||
#TODO(cp16net): need to set the return code correctly
|
||||
return wsgi.Result(views.InstanceView(instance).data(), 201)
|
||||
|
||||
@staticmethod
|
||||
def _validate_empty_body(body):
|
||||
"""Check that the body is not empty"""
|
||||
if not body:
|
||||
msg = "The request contains an empty body"
|
||||
raise rd_exceptions.ReddwarfError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _validate_volume_size(size):
|
||||
"""Validate the various possible errors for volume size"""
|
||||
try:
|
||||
volume_size = float(size)
|
||||
except (ValueError, TypeError) as err:
|
||||
LOG.error(err)
|
||||
msg = ("Required element/key - instance volume"
|
||||
"'size' was not specified as a number")
|
||||
raise rd_exceptions.ReddwarfError(msg)
|
||||
if int(volume_size) != volume_size or int(volume_size) < 1:
|
||||
msg = ("Volume 'size' needs to be a positive "
|
||||
"integer value, %s cannot be accepted."
|
||||
% volume_size)
|
||||
raise rd_exceptions.ReddwarfError(msg)
|
||||
#TODO(cp16net) add in the volume validation when volumes are supported
|
||||
# max_size = FLAGS.reddwarf_max_accepted_volume_size
|
||||
# if int(volume_size) > max_size:
|
||||
# msg = ("Volume 'size' cannot exceed maximum "
|
||||
# "of %d Gb, %s cannot be accepted."
|
||||
# % (max_size, volume_size))
|
||||
# raise rd_exceptions.ReddwarfError(msg)
|
||||
|
||||
@staticmethod
|
||||
def _validate(body):
|
||||
"""Validate that the request has all the required parameters"""
|
||||
InstanceController._validate_empty_body(body)
|
||||
try:
|
||||
body['instance']
|
||||
body['instance']['flavorRef']
|
||||
# TODO(cp16net) add in volume to the mix
|
||||
# volume_size = body['instance']['volume']['size']
|
||||
except KeyError as e:
|
||||
LOG.error("Create Instance Required field(s) - %s" % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
# Instance._validate_volume_size(volume_size)
|
||||
|
||||
@staticmethod
|
||||
def _validate_resize_instance(body):
|
||||
""" Validate that the resize body has the attributes for flavorRef """
|
||||
try:
|
||||
body['resize']
|
||||
body['resize']['flavorRef']
|
||||
except KeyError as e:
|
||||
LOG.error("Resize Instance Required field(s) - %s" % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
|
||||
@staticmethod
|
||||
def _validate_single_resize_in_body(body):
|
||||
# Validate body resize does not have both volume and flavorRef
|
||||
try:
|
||||
resize = body['resize']
|
||||
if 'volume' in resize and 'flavorRef' in resize:
|
||||
msg = ("Not allowed to resize volume "
|
||||
"and flavor at the same time")
|
||||
LOG.error(msg)
|
||||
raise rd_exceptions.ReddwarfError(msg)
|
||||
except KeyError as e:
|
||||
LOG.error("Resize Instance Required field(s) - %s" % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
|
||||
@staticmethod
|
||||
def _validate_resize(body, old_volume_size):
|
||||
"""
|
||||
We are going to check that volume resizing data is present.
|
||||
"""
|
||||
InstanceController._validate_empty_body(body)
|
||||
try:
|
||||
body['resize']
|
||||
body['resize']['volume']
|
||||
new_volume_size = body['resize']['volume']['size']
|
||||
except KeyError as e:
|
||||
LOG.error("Resize Instance Required field(s) - %s" % e)
|
||||
raise rd_exceptions.ReddwarfError("Required element/key - %s "
|
||||
"was not specified" % e)
|
||||
Instance._validate_volume_size(new_volume_size)
|
||||
if int(new_volume_size) <= old_volume_size:
|
||||
raise rd_exceptions.ReddwarfError("The new volume 'size' cannot "
|
||||
"be less than the current volume size "
|
||||
"of '%s'" % old_volume_size)
|
||||
|
||||
|
||||
class API(wsgi.Router):
|
||||
"""API"""
|
||||
|
@ -578,7 +578,7 @@ class RequestDeserializer(object):
|
||||
def deserialize_body(self, request, action):
|
||||
if not len(request.body) > 0:
|
||||
LOG.debug(_("Empty body provided in request"))
|
||||
return {}
|
||||
return self._return_empty_body(action)
|
||||
|
||||
try:
|
||||
content_type = request.get_content_type()
|
||||
@ -588,7 +588,7 @@ class RequestDeserializer(object):
|
||||
|
||||
if content_type is None:
|
||||
LOG.debug(_("No Content-Type provided in request"))
|
||||
return {}
|
||||
return self._return_empty_body(action)
|
||||
|
||||
try:
|
||||
deserializer = self.get_body_deserializer(content_type)
|
||||
@ -598,6 +598,12 @@ class RequestDeserializer(object):
|
||||
|
||||
return deserializer.deserialize(request.body, action)
|
||||
|
||||
def _return_empty_body(self, action):
|
||||
if action in ["create", "update", "action"]:
|
||||
return {'body': None}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def get_body_deserializer(self, content_type):
|
||||
try:
|
||||
return self.body_deserializers[content_type]
|
||||
|
@ -19,6 +19,7 @@ import novaclient
|
||||
|
||||
from reddwarf import tests
|
||||
from reddwarf.common import utils
|
||||
from reddwarf.common import exception
|
||||
from reddwarf.instance import models
|
||||
from reddwarf.instance.tasks import InstanceTasks
|
||||
from reddwarf.tests.factories import models as factory_models
|
||||
@ -88,3 +89,9 @@ class TestInstance(tests.BaseTest):
|
||||
self.assertEqual(instance['task_id'], InstanceTasks.BUILDING.code)
|
||||
self.assertEqual(instance['task_description'],
|
||||
InstanceTasks.BUILDING.db_text)
|
||||
|
||||
def test_create_instance_data_without_flavorref(self):
|
||||
#todo(cp16net) fix this to work with the factory
|
||||
self.mock_out_client()
|
||||
self.FAKE_SERVER.flavor = None
|
||||
self.assertRaises(exception.BadRequest, factory_models.Instance())
|
||||
|
@ -55,11 +55,12 @@ class TestInstanceController(ControllerTestBase):
|
||||
super(TestInstanceController, self).setUp()
|
||||
|
||||
# TODO(hub-cap): Start testing the failure cases
|
||||
# def test_show_broken(self):
|
||||
# response = self.app.get("%s/%s" % (self.instances_path,
|
||||
# self.DUMMY_INSTANCE_ID),
|
||||
# headers={'X-Auth-Token': '123'})
|
||||
# self.assertEqual(response.status_int, 404)
|
||||
def test_show_broken(self):
|
||||
raise SkipTest()
|
||||
response = self.app.get("%s/%s" % (self.instances_path,
|
||||
self.DUMMY_INSTANCE_ID),
|
||||
headers={'X-Auth-Token': '123'})
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_show(self):
|
||||
raise SkipTest()
|
||||
|
Loading…
Reference in New Issue
Block a user