diff --git a/bilean/common/exception.py b/bilean/common/exception.py index 5aff70d..2495ccd 100644 --- a/bilean/common/exception.py +++ b/bilean/common/exception.py @@ -92,57 +92,36 @@ class InvalidParameter(BileanException): msg_fmt = _("Invalid value '%(value)s' specified for '%(name)s'") -class ClusterNotFound(BileanException): - msg_fmt = _("The cluster (%(cluster)s) could not be found.") - - -class NodeNotFound(BileanException): - msg_fmt = _("The node (%(node)s) could not be found.") - - class RuleTypeNotFound(BileanException): msg_fmt = _("Rule type (%(rule_type)s) is not found.") -class ProfileTypeNotMatch(BileanException): +class RuleTypeNotMatch(BileanException): msg_fmt = _("%(message)s") -class ProfileNotFound(BileanException): - msg_fmt = _("The profile (%(profile)s) could not be found.") +class RuleNotFound(BileanException): + msg_fmt = _("The rule (%(rule)s) could not be found.") -class ProfileNotSpecified(BileanException): - msg_fmt = _("Profile not specified.") +class RuleNotSpecified(BileanException): + msg_fmt = _("Rule not specified.") -class ProfileOperationFailed(BileanException): +class RuleOperationFailed(BileanException): msg_fmt = _("%(message)s") -class ProfileOperationTimeout(BileanException): +class RuleOperationTimeout(BileanException): msg_fmt = _("%(message)s") -class PolicyNotSpecified(BileanException): - msg_fmt = _("Policy not specified.") - - -class PolicyTypeNotFound(BileanException): - msg_fmt = _("Policy type (%(policy_type)s) is not found.") - - class PolicyNotFound(BileanException): msg_fmt = _("The policy (%(policy)s) could not be found.") -class PolicyBindingNotFound(BileanException): - msg_fmt = _("The policy (%(policy)s) is not found attached to the " - "specified cluster (%(identity)s).") - - -class PolicyTypeConflict(BileanException): - msg_fmt = _("The policy with type (%(policy_type)s) already exists.") +class UserNotFound(BileanException): + msg_fmt = _("The user (%(user)s) could not be found.") class InvalidSchemaError(BileanException): @@ -176,23 +155,6 @@ class RequestLimitExceeded(BileanException): msg_fmt = _('Request limit exceeded: %(message)s') -class WebhookNotFound(BileanException): - msg_fmt = _("The webhook (%(webhook)s) could not be found.") - - -class ReceiverNotFound(BileanException): - msg_fmt = _("The receiver (%(receiver)s) could not be found.") - - -class ActionNotFound(BileanException): - msg_fmt = _("The action (%(action)s) could not be found.") - - -class ActionInProgress(BileanException): - msg_fmt = _("Cluster %(cluster_name)s already has an action (%(action)s) " - "in progress.") - - class EventNotFound(BileanException): msg_fmt = _("The event (%(event)s) could not be found.") @@ -227,23 +189,12 @@ class TrustNotFound(InternalError): msg_fmt = _("The trust for trustor (%(trustor)s) could not be found.") -class ResourceCreationFailure(InternalError): - # Used when creating resources in other services - msg_fmt = _("Failed in creating %(rtype)s.") - - -class ResourceUpdateFailure(InternalError): - # Used when updating resources from other services - msg_fmt = _("Failed in updating %(resource)s.") - - class ResourceDeletionFailure(InternalError): # Used when deleting resources from other services msg_fmt = _("Failed in deleting %(resource)s.") class ResourceNotFound(InternalError): - # Used when retrieving resources from other services msg_fmt = _("The resource (%(resource)s) could not be found.") @@ -260,11 +211,6 @@ class InvalidSpec(InternalError): msg_fmt = _("%(message)s") -class PolicyNotAttached(InternalError): - msg_fmt = _("The policy (%(policy)s) is not attached to the specified " - "cluster (%(cluster)s).") - - class HTTPExceptionDisguise(Exception): """Disguises HTTP exceptions. diff --git a/bilean/db/sqlalchemy/api.py b/bilean/db/sqlalchemy/api.py index 8e0b91c..aab6e25 100644 --- a/bilean/db/sqlalchemy/api.py +++ b/bilean/db/sqlalchemy/api.py @@ -24,7 +24,6 @@ from sqlalchemy.orm.session import Session from bilean.common import consts from bilean.common import exception -from bilean.common.i18n import _ from bilean.db.sqlalchemy import filters as db_filters from bilean.db.sqlalchemy import migration from bilean.db.sqlalchemy import models @@ -133,14 +132,12 @@ def user_get(context, user_id, show_deleted=False, tenant_safe=True): def user_update(context, user_id, values): user = user_get(context, user_id) - if not user: - raise exception.NotFound(_('Attempt to update a user with id: ' - '%(id)s %(msg)s') % { - 'id': user_id, - 'msg': 'that does not exist'}) + if user is None: + raise exception.UserNotFound(user=user_id) user.update(values) user.save(_session(context)) + return user def user_create(context, values): @@ -153,11 +150,9 @@ def user_create(context, values): def user_delete(context, user_id): session = _session(context) user = user_get(context, user_id) - if not user: - raise exception.NotFound(_('Attempt to delete a user with id: ' - '%(id)s %(msg)s') % { - 'id': user_id, - 'msg': 'that does not exist'}) + if user is None: + return + # Delete all related resource records for resource in user.resources: session.delete(resource) @@ -196,7 +191,7 @@ def user_get_all(context, show_deleted=False, limit=None, def rule_get(context, rule_id, show_deleted=False): query = model_query(context, models.Rule) - rule = query.get(rule_id) + rule = query.filter_by(id=rule_id).first() deleted_ok = show_deleted or context.show_deleted if rule is None or rule.deleted_at is not None and not deleted_ok: @@ -239,11 +234,8 @@ def rule_create(context, values): def rule_update(context, rule_id, values): rule = rule_get(context, rule_id) - if not rule: - raise exception.NotFound(_('Attempt to update a rule with id: ' - '%(id)s %(msg)s') % { - 'id': rule_id, - 'msg': 'that does not exist'}) + if rule is None: + raise exception.RuleNotFound(rule=rule_id) rule.update(values) rule.save(_session(context)) @@ -252,11 +244,9 @@ def rule_update(context, rule_id, values): def rule_delete(context, rule_id): rule = rule_get(context, rule_id) - if not rule: - raise exception.NotFound(_('Attempt to delete a rule with id: ' - '%(id)s %(msg)s') % { - 'id': rule_id, - 'msg': 'that does not exist'}) + if rule is None: + return + session = Session.object_session(rule) rule.soft_delete(session=session) session.flush() @@ -315,11 +305,8 @@ def resource_create(context, values): def resource_update(context, resource_id, values): resource = resource_get(context, resource_id) - if not resource: - raise exception.NotFound(_('Attempt to update a resource with id: ' - '%(id)s %(msg)s') % { - 'id': resource_id, - 'msg': 'that does not exist'}) + if resource is None: + raise exception.ResourceNotFound(resource=resource_id) resource.update(values) resource.save(_session(context)) @@ -329,11 +316,9 @@ def resource_update(context, resource_id, values): def resource_delete(context, resource_id, soft_delete=True): resource = resource_get(context, resource_id) - if not resource: - raise exception.NotFound(_('Attempt to delete a resource with id: ' - '%(id)s %(msg)s') % { - 'id': resource_id, - 'msg': 'that does not exist'}) + if resource is None: + return + session = Session.object_session(resource) if soft_delete: resource.soft_delete(session=session) @@ -416,10 +401,8 @@ def job_delete(context, job_id): job = model_query(context, models.Job).get(job_id) if job is None: - raise exception.NotFound(_('Attempt to delete a job with id: ' - '%(id)s %(msg)s') % { - 'id': job_id, - 'msg': 'that does not exist'}) + return + session = Session.object_session(job) session.delete(job) session.flush() @@ -469,11 +452,8 @@ def policy_create(context, values): def policy_update(context, policy_id, values): policy = policy_get(context, policy_id) - if not policy: - raise exception.NotFound(_('Attempt to update a policy with id: ' - '%(id)s %(msg)s') % { - 'id': policy_id, - 'msg': 'that does not exist'}) + if policy is None: + raise exception.PolicyNotFound(policy=policy_id) policy.update(values) policy.save(_session(context)) @@ -482,11 +462,9 @@ def policy_update(context, policy_id, values): def policy_delete(context, policy_id): policy = policy_get(context, policy_id) - if not policy: - raise exception.NotFound(_('Attempt to delete a policy with id: ' - '%(id)s %(msg)s') % { - 'id': policy_id, - 'msg': 'that does not exist'}) + if policy is None: + return + session = Session.object_session(policy) policy.soft_delete(session=session) session.flush() diff --git a/bilean/db/sqlalchemy/models.py b/bilean/db/sqlalchemy/models.py index f7b52d8..fa81bf7 100644 --- a/bilean/db/sqlalchemy/models.py +++ b/bilean/db/sqlalchemy/models.py @@ -14,12 +14,13 @@ SQLAlchemy models for Bilean data. """ -import uuid +import six from bilean.db.sqlalchemy import types from oslo_db.sqlalchemy import models from oslo_utils import timeutils +from oslo_utils import uuidutils import sqlalchemy from sqlalchemy.ext.declarative import declarative_base @@ -28,6 +29,7 @@ from sqlalchemy.orm import relationship from sqlalchemy.orm.session import Session BASE = declarative_base() +UUID4 = uuidutils.generate_uuid def get_session(): @@ -70,7 +72,7 @@ class BileanBase(models.ModelBase): if not session: session = get_session() session.begin() - for k, v in values.iteritems(): + for k, v in six.iteritems(values): setattr(self, k, v) session.commit() @@ -121,7 +123,7 @@ class Policy(BASE, BileanBase, SoftDelete, models.TimestampMixin): __tablename__ = 'policy' id = sqlalchemy.Column(sqlalchemy.String(36), primary_key=True, - default=lambda: str(uuid.uuid4())) + default=lambda: UUID4()) name = sqlalchemy.Column(sqlalchemy.String(255)) rules = sqlalchemy.Column(types.List) is_default = sqlalchemy.Column(sqlalchemy.Boolean, default=False) @@ -133,7 +135,7 @@ class Rule(BASE, BileanBase, SoftDelete, models.TimestampMixin): __tablename__ = 'rule' id = sqlalchemy.Column(sqlalchemy.String(36), primary_key=True, - default=lambda: str(uuid.uuid4())) + default=lambda: UUID4()) name = sqlalchemy.Column(sqlalchemy.String(255)) type = sqlalchemy.Column(sqlalchemy.String(255)) spec = sqlalchemy.Column(types.Dict) @@ -145,7 +147,7 @@ class Resource(BASE, BileanBase, SoftDelete, models.TimestampMixin): __tablename__ = 'resource' id = sqlalchemy.Column(sqlalchemy.String(36), primary_key=True, - default=lambda: str(uuid.uuid4())) + default=lambda: UUID4()) user_id = sqlalchemy.Column( sqlalchemy.String(36), sqlalchemy.ForeignKey('user.id'), @@ -166,8 +168,7 @@ class Event(BASE, BileanBase, SoftDelete): __tablename__ = 'event' id = sqlalchemy.Column(sqlalchemy.String(36), primary_key=True, - default=lambda: str(uuid.uuid4()), - unique=True) + default=lambda: UUID4(), unique=True) user_id = sqlalchemy.Column(sqlalchemy.String(36), sqlalchemy.ForeignKey('user.id'), nullable=False) diff --git a/bilean/rules/base.py b/bilean/rules/base.py index 1b65fca..7ab270f 100644 --- a/bilean/rules/base.py +++ b/bilean/rules/base.py @@ -122,15 +122,16 @@ class Rule(object): return cls.from_db_record(rule) @classmethod - def load_all(cls, context, show_deleted=False, limit=None, - marker=None, sort_keys=None, sort_dir=None, - filters=None): + def load_all(cls, context, limit=None, marker=None, sort_keys=None, + sort_dir=None, filters=None, show_deleted=False): '''Retrieve all rules from database.''' - records = db_api.rule_get_all(context, show_deleted=show_deleted, - limit=limit, marker=marker, - sort_keys=sort_keys, sort_dir=sort_dir, - filters=filters) + records = db_api.rule_get_all(context, limit=limit, + marker=marker, + sort_keys=sort_keys, + sort_dir=sort_dir, + filters=filters, + show_deleted=show_deleted) return [cls.from_db_record(record) for record in records] @@ -177,16 +178,6 @@ class Rule(object): return NotImplemented - def do_delete(self, obj): - '''For subclass to override.''' - - return NotImplemented - - def do_update(self, obj, new_rule, **params): - '''For subclass to override.''' - - return NotImplemented - def to_dict(self): rule_dict = { 'id': self.id, diff --git a/bilean/tests/rules/test_rule_base.py b/bilean/tests/rules/test_rule_base.py index deedc34..001ffbe 100644 --- a/bilean/tests/rules/test_rule_base.py +++ b/bilean/tests/rules/test_rule_base.py @@ -11,12 +11,11 @@ # under the License. import mock -from oslo_utils import encodeutils import six from bilean.common import exception -from bilean.common.i18n import _ from bilean.common import schema +from bilean.common import utils as common_utils from bilean.db import api as db_api from bilean.engine import environment from bilean.rules import base as rule_base @@ -106,3 +105,159 @@ class TestRuleBase(base.BileanTestCase): self.assertRaises(exception.RuleTypeNotFound, rule_base.Rule, 'test-rule', bad_spec) + + def test_load(self): + rule = self._create_db_rule() + result = rule_base.Rule.load(self.context, rule.id) + + self.assertEqual(rule.id, result.id) + self.assertEqual(rule.name, result.name) + self.assertEqual(rule.type, result.type) + self.assertEqual(rule.spec, result.spec) + self.assertEqual(rule.meta_data, result.metadata) + self.assertEqual({'key1': 'value1', 'key2': 2}, result.properties) + + self.assertEqual(rule.created_at, result.created_at) + self.assertEqual(rule.updated_at, result.updated_at) + + def test_load_not_found(self): + ex = self.assertRaises(exception.RuleNotFound, + rule_base.Rule.load, + self.context, 'fake-rule', None) + self.assertEqual('The rule (fake-rule) could not be found.', + six.text_type(ex)) + + ex = self.assertRaises(exception.RuleNotFound, + rule_base.Rule.load, + self.context, None, None) + self.assertEqual('The rule (None) could not be found.', + six.text_type(ex)) + + def test_load_all(self): + result = rule_base.Rule.load_all(self.context) + self.assertEqual([], list(result)) + + rule1 = self._create_db_rule(name='rule-1', id='ID1') + rule2 = self._create_db_rule(name='rule-2', id='ID2') + + result = rule_base.Rule.load_all(self.context) + rules = list(result) + self.assertEqual(2, len(rules)) + self.assertEqual(rule1.id, rules[0].id) + self.assertEqual(rule2.id, rules[1].id) + + @mock.patch.object(db_api, 'rule_get_all') + def test_load_all_with_params(self, mock_get_all): + mock_get_all.return_value = [] + + res = list(rule_base.Rule.load_all(self.context)) + self.assertEqual([], res) + mock_get_all.assert_called_once_with(self.context, limit=None, + marker=None, sort_keys=None, + sort_dir=None, filters=None, + show_deleted=False) + mock_get_all.reset_mock() + + res = list(rule_base.Rule.load_all(self.context, limit=1, + marker='MARKER', + sort_keys=['K1'], + sort_dir='asc', + filters={'name': 'fake-name'})) + self.assertEqual([], res) + mock_get_all.assert_called_once_with(self.context, limit=1, + marker='MARKER', + sort_keys=['K1'], + sort_dir='asc', + filters={'name': 'fake-name'}, + show_deleted=False) + + def test_delete(self): + rule = self._create_db_rule() + rule_id = rule.id + + res = rule_base.Rule.delete(self.context, rule_id) + self.assertIsNone(res) + self.assertRaises(exception.RuleNotFound, + rule_base.Rule.load, + self.context, rule_id, None) + + def test_delete_not_found(self): + result = rule_base.Rule.delete(self.context, 'fake-rule') + self.assertIsNone(result) + + def test_store_for_create(self): + rule = self._create_rule('test-rule') + self.assertIsNone(rule.id) + + rule_id = rule.store(self.context) + self.assertIsNotNone(rule_id) + self.assertEqual(rule_id, rule.id) + + result = db_api.rule_get(self.context, rule_id) + + self.assertIsNotNone(result) + self.assertEqual('test-rule', result.name) + self.assertEqual(rule_id, result.id) + self.assertEqual(rule.type, result.type) + self.assertEqual(rule.spec, result.spec) + self.assertEqual(rule.metadata, result.meta_data) + + self.assertIsNotNone(result.created_at) + self.assertIsNone(result.updated_at) + + def test_store_for_update(self): + rule = self._create_rule('test-rule') + self.assertIsNone(rule.id) + rule_id = rule.store(self.context) + self.assertIsNotNone(rule_id) + self.assertEqual(rule_id, rule.id) + + rule.name = 'test-rule-1' + rule.metadata = {'key': 'value'} + + new_id = rule.store(self.context) + self.assertEqual(rule_id, new_id) + + result = db_api.rule_get(self.context, rule_id) + self.assertIsNotNone(result) + self.assertEqual('test-rule-1', result.name) + self.assertEqual({'key': 'value'}, result.meta_data) + self.assertIsNotNone(rule.created_at) + self.assertIsNotNone(rule.updated_at) + + def test_to_dict(self): + rule = self._create_rule('test-rule') + rule_id = rule.store(self.context) + self.assertIsNotNone(rule_id) + expected = { + 'id': rule_id, + 'name': rule.name, + 'type': rule.type, + 'spec': rule.spec, + 'metadata': rule.metadata, + 'created_at': common_utils.format_time(rule.created_at), + 'updated_at': None, + 'deleted_at': None, + } + + result = rule_base.Rule.load(self.context, rule_id=rule.id) + self.assertEqual(expected, result.to_dict()) + + def test_get_schema(self): + expected = { + 'key1': { + 'default': 'value1', + 'description': 'First key', + 'readonly': False, + 'required': False, + 'type': 'String' + }, + 'key2': { + 'description': 'Second key', + 'readonly': False, + 'required': True, + 'type': 'Integer' + }, + } + res = DummyRule.get_schema() + self.assertEqual(expected, res)