Add data module for checkpoint verification API
Change-Id: If672a14b932b33a4254f21486d2e3fbe56198a73 Implements: blueprint support-verify-the-checkpoint-api
This commit is contained in:
parent
84e22dee08
commit
33756861da
|
@ -533,6 +533,48 @@ def restore_get_all_by_project(context, project_id, marker, limit,
|
|||
offset=offset)
|
||||
|
||||
|
||||
def verification_get(context, verification_id):
|
||||
"""Get a verification or raise if it does not exist."""
|
||||
return IMPL.verification_get(context, verification_id)
|
||||
|
||||
|
||||
def verification_create(context, values):
|
||||
"""Create a verification from the values dictionary."""
|
||||
return IMPL.verification_create(context, values)
|
||||
|
||||
|
||||
def verification_update(context, verification_id, values):
|
||||
"""Set the given properties on a verification and update it.
|
||||
|
||||
Raises NotFound if verification does not exist.
|
||||
|
||||
"""
|
||||
return IMPL.verification_update(context, verification_id,
|
||||
values)
|
||||
|
||||
|
||||
def verification_destroy(context, verification_id):
|
||||
"""Destroy the verification or raise if it does not exist."""
|
||||
return IMPL.verification_destroy(context, verification_id)
|
||||
|
||||
|
||||
def verification_get_all(context, marker, limit, sort_keys=None,
|
||||
sort_dirs=None, filters=None, offset=None):
|
||||
"""Get all verifications."""
|
||||
return IMPL.verification_get_all(
|
||||
context, marker, limit, sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs, filters=filters, offset=offset)
|
||||
|
||||
|
||||
def verification_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_keys=None, sort_dirs=None,
|
||||
filters=None, offset=None):
|
||||
"""Get all verifications belonging to a project."""
|
||||
return IMPL.verification_get_all_by_project(
|
||||
context, project_id, marker, limit, sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs, filters=filters, offset=offset)
|
||||
|
||||
|
||||
def operation_log_get(context, operation_log_id):
|
||||
"""Get a operation log or raise if it does not exist."""
|
||||
return IMPL.operation_log_get(context, operation_log_id)
|
||||
|
|
|
@ -1289,7 +1289,7 @@ def _operation_log_get(context, operation_log_id, session=None):
|
|||
id=operation_log_id
|
||||
).first()
|
||||
if not result:
|
||||
raise exception.OperationLogNotFound(restore_id=operation_log_id)
|
||||
raise exception.OperationLogNotFound(operation_log_id=operation_log_id)
|
||||
|
||||
return result
|
||||
|
||||
|
@ -1413,6 +1413,154 @@ def _process_operation_log_filters(query, filters):
|
|||
###############################
|
||||
|
||||
|
||||
@require_context
|
||||
def verification_create(context, values):
|
||||
verification_ref = models.Verification()
|
||||
if not values.get('id'):
|
||||
values['id'] = uuidutils.generate_uuid()
|
||||
verification_ref.update(values)
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
verification_ref.save(session)
|
||||
return verification_ref
|
||||
|
||||
|
||||
@require_context
|
||||
def verification_get(context, verification_id):
|
||||
return _verification_get(context, verification_id)
|
||||
|
||||
|
||||
@require_context
|
||||
def _verification_get(context, verification_id, session=None):
|
||||
result = model_query(
|
||||
context,
|
||||
models.Verification,
|
||||
session=session
|
||||
).filter_by(
|
||||
id=verification_id
|
||||
).first()
|
||||
if not result:
|
||||
raise exception.VerificationNotFound(
|
||||
verification_id=verification_id)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@require_context
|
||||
def verification_update(context, verification_id, values):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
verification_ref = _verification_get(
|
||||
context, verification_id, session=session)
|
||||
verification_ref.update(values)
|
||||
return verification_ref
|
||||
|
||||
|
||||
@require_context
|
||||
@_retry_on_deadlock
|
||||
def verification_destroy(context, verification_id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
verification_ref = _verification_get(context,
|
||||
verification_id,
|
||||
session=session)
|
||||
verification_ref.delete(session=session)
|
||||
|
||||
|
||||
def _verification_get_query(context, session=None, project_only=False):
|
||||
return model_query(context, models.Verification, session=session,
|
||||
project_only=project_only)
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def verification_get_all(context, marker, limit, sort_keys=None,
|
||||
sort_dirs=None, filters=None, offset=None):
|
||||
"""Retrieves all verifications.
|
||||
|
||||
If no sort parameters are specified then the returned plans are sorted
|
||||
first by the 'created_at' key and then by the 'id' key in descending
|
||||
order.
|
||||
|
||||
:param context: context to query under
|
||||
:param marker: the last item of the previous page, used to determine the
|
||||
next page of results to return
|
||||
:param limit: maximum number of items to return
|
||||
:param sort_keys: list of attributes by which results should be sorted,
|
||||
paired with corresponding item in sort_dirs
|
||||
:param sort_dirs: list of directions in which results should be sorted,
|
||||
paired with corresponding item in sort_keys
|
||||
:param filters: dictionary of filters; values that are in lists, tuples,
|
||||
or sets cause an 'IN' operation, while exact matching
|
||||
is used for other values, see _process_verification_filters
|
||||
function for more information
|
||||
:param offset: number of items to skip
|
||||
:returns: list of matching verifications
|
||||
"""
|
||||
if filters and not is_valid_model_filters(models.Verification, filters):
|
||||
return []
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
query = _generate_paginate_query(context, session, marker, limit,
|
||||
sort_keys, sort_dirs, filters,
|
||||
offset, models.Verification)
|
||||
if query is None:
|
||||
return []
|
||||
return query.all()
|
||||
|
||||
|
||||
@require_context
|
||||
def verification_get_all_by_project(context, project_id, marker, limit,
|
||||
sort_keys=None, sort_dirs=None,
|
||||
filters=None, offset=None):
|
||||
"""Retrieves all verifications in a project.
|
||||
|
||||
If no sort parameters are specified then the returned plans are sorted
|
||||
first by the 'created_at' key and then by the 'id' key in descending
|
||||
order.
|
||||
|
||||
:param context: context to query under
|
||||
:param project_id: project for all verifications being retrieved
|
||||
:param marker: the last item of the previous page, used to determine the
|
||||
next page of results to return
|
||||
:param limit: maximum number of items to return
|
||||
:param sort_keys: list of attributes by which results should be sorted,
|
||||
paired with corresponding item in sort_dirs
|
||||
:param sort_dirs: list of directions in which results should be sorted,
|
||||
paired with corresponding item in sort_keys
|
||||
:param filters: dictionary of filters; values that are in lists, tuples,
|
||||
or sets cause an 'IN' operation, while exact matching
|
||||
is used for other values, see _process_verification_filters
|
||||
function for more information
|
||||
:param offset: number of items to skip
|
||||
:returns: list of matching verifications
|
||||
"""
|
||||
if filters and not is_valid_model_filters(models.Verification, filters):
|
||||
return []
|
||||
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
authorize_project_context(context, project_id)
|
||||
filters = filters.copy() if filters else {}
|
||||
filters['project_id'] = project_id
|
||||
query = _generate_paginate_query(context, session, marker, limit,
|
||||
sort_keys, sort_dirs, filters,
|
||||
offset, models.Verification)
|
||||
if query is None:
|
||||
return []
|
||||
return query.all()
|
||||
|
||||
|
||||
def _process_verification_filters(query, filters):
|
||||
if filters:
|
||||
if not is_valid_model_filters(models.Verification, filters):
|
||||
return None
|
||||
query = query.filter_by(**filters)
|
||||
return query
|
||||
###############################
|
||||
|
||||
|
||||
@require_context
|
||||
def checkpoint_record_create(context, values):
|
||||
checkpoint_record_ref = models.CheckpointRecord()
|
||||
|
@ -1585,6 +1733,10 @@ PAGINATION_HELPERS = {
|
|||
models.Plan: (_plan_get_query, _process_plan_filters, _plan_get),
|
||||
models.Restore: (_restore_get_query, _process_restore_filters,
|
||||
_restore_get),
|
||||
models.Verification: (
|
||||
_verification_get_query,
|
||||
_process_verification_filters,
|
||||
_verification_get),
|
||||
models.Trigger: (_trigger_list_query, _trigger_list_process_filters,
|
||||
_trigger_get),
|
||||
models.TriggerExecution: (_trigger_execution_list_query,
|
||||
|
@ -1607,7 +1759,6 @@ PAGINATION_HELPERS = {
|
|||
_scheduled_operation_log_list_query,
|
||||
_scheduled_operation_log_list_process_filters,
|
||||
_scheduled_operation_log_get),
|
||||
|
||||
models.CheckpointRecord: (
|
||||
_checkpoint_record_list_query,
|
||||
_checkpoint_record_list_process_filters,
|
||||
|
|
|
@ -266,6 +266,10 @@ class RestoreNotFound(NotFound):
|
|||
message = _("Restore %(restore_id)s could not be found.")
|
||||
|
||||
|
||||
class VerificationNotFound(NotFound):
|
||||
message = _("Verification %(verification_id)s could not be found.")
|
||||
|
||||
|
||||
class OperationLogNotFound(NotFound):
|
||||
message = _("OperationLog %(restore_id)s could not be found.")
|
||||
|
||||
|
|
|
@ -24,3 +24,4 @@ def register_all():
|
|||
__import__('karbor.objects.restore')
|
||||
__import__('karbor.objects.operation_log')
|
||||
__import__('karbor.objects.checkpoint_record')
|
||||
__import__('karbor.objects.verification')
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
# 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.
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_versionedobjects import fields
|
||||
import six
|
||||
|
||||
from karbor import db
|
||||
from karbor import exception
|
||||
from karbor.i18n import _
|
||||
from karbor import objects
|
||||
from karbor.objects import base
|
||||
|
||||
|
||||
@base.KarborObjectRegistry.register
|
||||
class Verification(base.KarborPersistentObject, base.KarborObject,
|
||||
base.KarborObjectDictCompat,
|
||||
base.KarborComparableObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.UUIDField(),
|
||||
'project_id': fields.UUIDField(),
|
||||
'provider_id': fields.UUIDField(),
|
||||
'checkpoint_id': fields.UUIDField(),
|
||||
'parameters': base.DictOfDictOfStringsField(nullable=True),
|
||||
'status': fields.StringField(nullable=True),
|
||||
'resources_status': fields.DictOfStringsField(nullable=True),
|
||||
'resources_reason': fields.DictOfStringsField(nullable=True),
|
||||
}
|
||||
|
||||
json_fields = ('parameters', 'resources_status', 'resources_reason')
|
||||
|
||||
@classmethod
|
||||
def _from_db_object(cls, context, verification, db_verification):
|
||||
for name, field in verification.fields.items():
|
||||
value = db_verification.get(name)
|
||||
if isinstance(field, fields.IntegerField):
|
||||
value = value or 0
|
||||
elif isinstance(field, fields.DateTimeField):
|
||||
value = value or None
|
||||
if name in cls.json_fields:
|
||||
value = jsonutils.loads(value) if value else {}
|
||||
verification[name] = value
|
||||
|
||||
verification._context = context
|
||||
verification.obj_reset_changes()
|
||||
return verification
|
||||
|
||||
@classmethod
|
||||
def _convert_properties_to_db_format(cls, updates):
|
||||
for attr in cls.json_fields:
|
||||
value = updates.pop(attr, None)
|
||||
if value:
|
||||
updates[attr] = jsonutils.dumps(value)
|
||||
|
||||
@base.remotable
|
||||
def create(self):
|
||||
if self.obj_attr_is_set('id'):
|
||||
raise exception.ObjectActionError(action='create',
|
||||
reason=_('already created'))
|
||||
updates = self.karbor_obj_get_changes()
|
||||
self._convert_properties_to_db_format(updates)
|
||||
db_verification = db.verification_create(self._context, updates)
|
||||
self._from_db_object(self._context, self, db_verification)
|
||||
|
||||
@base.remotable
|
||||
def save(self):
|
||||
updates = self.karbor_obj_get_changes()
|
||||
self._convert_properties_to_db_format(updates)
|
||||
if updates:
|
||||
db.verification_update(self._context, self.id, updates)
|
||||
self.obj_reset_changes()
|
||||
|
||||
@base.remotable
|
||||
def destroy(self):
|
||||
with self.obj_as_admin():
|
||||
db.verification_destroy(self._context, self.id)
|
||||
|
||||
@base.remotable
|
||||
def update_resource_status(self, resource_type, resource_id, status,
|
||||
reason=None):
|
||||
key = '{}#{}'.format(resource_type, resource_id)
|
||||
if not self.obj_attr_is_set('resources_status'):
|
||||
self.resources_status = {}
|
||||
self.resources_status[key] = status
|
||||
self._changed_fields.add('resources_status')
|
||||
if isinstance(reason, six.string_types):
|
||||
if not self.obj_attr_is_set('resources_reason'):
|
||||
self.resources_reason = {}
|
||||
self.resources_reason[key] = reason
|
||||
self._changed_fields.add('resources_reason')
|
||||
|
||||
|
||||
@base.KarborObjectRegistry.register
|
||||
class VerificationList(base.ObjectListBase, base.KarborObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('verification'),
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all(cls, context, marker, limit, sort_keys=None, sort_dirs=None,
|
||||
filters=None, offset=None):
|
||||
verifications = db.verification_get_all(context, marker, limit,
|
||||
sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
filters=filters, offset=offset)
|
||||
return base.obj_make_list(context, cls(context), objects.Verification,
|
||||
verifications)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all_by_project(cls, context, project_id, marker, limit,
|
||||
sort_keys=None, sort_dirs=None, filters=None,
|
||||
offset=None):
|
||||
verifications = db.verification_get_all_by_project(
|
||||
context, project_id, marker,
|
||||
limit, sort_keys=sort_keys,
|
||||
sort_dirs=sort_dirs,
|
||||
filters=filters,
|
||||
offset=offset)
|
||||
return base.obj_make_list(context, cls(context), objects.Verification,
|
||||
verifications)
|
|
@ -541,6 +541,56 @@ class RestoreDbTestCase(ModelBaseTestCase):
|
|||
self.ctxt, 42, {})
|
||||
|
||||
|
||||
class VerificationDbTestCase(ModelBaseTestCase):
|
||||
"""Unit tests for karbor.db.api.verification_*."""
|
||||
|
||||
fake_verification = {
|
||||
"id": "36ea41b2-c358-48a7-9117-70cb7617410a",
|
||||
"project_id": "586cc6ce-e286-40bd-b2b5-dd32694d9944",
|
||||
"provider_id": "2220f8b1-975d-4621-a872-fa9afb43cb6c",
|
||||
"checkpoint_id": "09edcbdc-d1c2-49c1-a212-122627b20968",
|
||||
"parameters": "{'username': 'admin'}",
|
||||
"status": "SUCCESS"
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(VerificationDbTestCase, self).setUp()
|
||||
self.ctxt = context.get_admin_context()
|
||||
|
||||
def test_verification_create(self):
|
||||
verification = db.verification_create(self.ctxt,
|
||||
self.fake_verification)
|
||||
self.assertTrue(uuidutils.is_uuid_like(verification['id']))
|
||||
self.assertEqual('SUCCESS', verification.status)
|
||||
|
||||
def test_verification_get(self):
|
||||
verification = db.verification_create(self.ctxt,
|
||||
self.fake_verification)
|
||||
self._assertEqualObjects(verification, db.verification_get(
|
||||
self.ctxt, verification['id']))
|
||||
|
||||
def test_verification_destroy(self):
|
||||
verification = db.verification_create(self.ctxt,
|
||||
self.fake_verification)
|
||||
db.verification_destroy(self.ctxt, verification['id'])
|
||||
self.assertRaises(exception.VerificationNotFound,
|
||||
db.verification_get,
|
||||
self.ctxt, verification['id'])
|
||||
|
||||
def test_verification_update(self):
|
||||
verification = db.verification_create(self.ctxt,
|
||||
self.fake_verification)
|
||||
db.verification_update(self.ctxt, verification['id'],
|
||||
{'status': 'INIT'})
|
||||
verification = db.verification_get(self.ctxt, verification['id'])
|
||||
self.assertEqual('INIT', verification['status'])
|
||||
|
||||
def test_verification_update_nonexistent(self):
|
||||
self.assertRaises(exception.VerificationNotFound,
|
||||
db.verification_update,
|
||||
self.ctxt, 42, {})
|
||||
|
||||
|
||||
class OperationLogTestCase(ModelBaseTestCase):
|
||||
"""Unit tests for karbor.db.api.operation_log_*."""
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# 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.
|
||||
|
||||
from oslo_versionedobjects import fields
|
||||
|
||||
from karbor import objects
|
||||
|
||||
|
||||
def fake_db_verification(**updates):
|
||||
db_verification = {
|
||||
"id": "36ea41b2-c358-48a7-9117-70cb7617410a",
|
||||
"project_id": "586cc6ce-e286-40bd-b2b5-dd32694d9944",
|
||||
"provider_id": "2220f8b1-975d-4621-a872-fa9afb43cb6c",
|
||||
"checkpoint_id": "09edcbdc-d1c2-49c1-a212-122627b20968",
|
||||
"parameters": '{}',
|
||||
"status": "SUCCESS"
|
||||
}
|
||||
for name, field in objects.Verification.fields.items():
|
||||
if name in db_verification:
|
||||
continue
|
||||
if field.nullable:
|
||||
db_verification[name] = None
|
||||
elif field.default != fields.UnspecifiedDefault:
|
||||
db_verification[name] = field.default
|
||||
else:
|
||||
raise Exception('fake_db_verification needs help with %s.' % name)
|
||||
|
||||
if updates:
|
||||
db_verification.update(updates)
|
||||
|
||||
return db_verification
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2015 SimpliVity Corp.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import mock
|
||||
|
||||
from karbor import objects
|
||||
from karbor.tests.unit import fake_verification
|
||||
from karbor.tests.unit import objects as test_objects
|
||||
|
||||
|
||||
class TestVerification(test_objects.BaseObjectsTestCase):
|
||||
@staticmethod
|
||||
def _compare(test, db, obj):
|
||||
db = {k: v for k, v in db.items()}
|
||||
test_objects.BaseObjectsTestCase._compare(test, db, obj)
|
||||
|
||||
@mock.patch('karbor.objects.Verification.get_by_id')
|
||||
def test_get_by_id(self, verification_get):
|
||||
db_verification = fake_verification.fake_db_verification()
|
||||
verification_get.return_value = db_verification
|
||||
verification = objects.Verification.get_by_id(self.context, "1")
|
||||
verification_get.assert_called_once_with(self.context, "1")
|
||||
self._compare(self, db_verification, verification)
|
||||
|
||||
@mock.patch('karbor.db.sqlalchemy.api.verification_create')
|
||||
def test_create(self, verification_create):
|
||||
db_verification = fake_verification.fake_db_verification()
|
||||
verification_create.return_value = db_verification
|
||||
verification = objects.Verification(context=self.context)
|
||||
verification.create()
|
||||
self.assertEqual(db_verification['id'], verification.id)
|
||||
|
||||
@mock.patch('karbor.db.sqlalchemy.api.verification_update')
|
||||
def test_save(self, verification_update):
|
||||
db_verification = fake_verification.fake_db_verification()
|
||||
verification = objects.Verification._from_db_object(
|
||||
self.context, objects.Verification(), db_verification)
|
||||
verification.status = 'FAILED'
|
||||
verification.save()
|
||||
verification_update.assert_called_once_with(
|
||||
self.context, verification.id, {'status': 'FAILED'})
|
||||
|
||||
@mock.patch('karbor.db.sqlalchemy.api.verification_destroy')
|
||||
def test_destroy(self, verification_destroy):
|
||||
db_verification = fake_verification.fake_db_verification()
|
||||
verification = objects.Verification._from_db_object(
|
||||
self.context, objects.Verification(), db_verification)
|
||||
verification.destroy()
|
||||
self.assertTrue(verification_destroy.called)
|
||||
admin_context = verification_destroy.call_args[0][0]
|
||||
self.assertTrue(admin_context.is_admin)
|
||||
|
||||
def test_obj_field_status(self):
|
||||
verification = objects.Verification(context=self.context,
|
||||
status='FAILED')
|
||||
self.assertEqual('FAILED', verification.status)
|
Loading…
Reference in New Issue