diff --git a/heat/engine/resources/os_database.py b/heat/engine/resources/os_database.py index c851d11bb3..7e925afaf2 100644 --- a/heat/engine/resources/os_database.py +++ b/heat/engine/resources/os_database.py @@ -11,6 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. +from troveclient.openstack.common.apiclient import exceptions as troveexc + from heat.common import exception from heat.engine import attributes from heat.engine.clients import troveclient @@ -29,6 +31,14 @@ class OSDBInstance(resource.Resource): OpenStack cloud database instance resource. ''' + TROVE_STATUS = ( + ERROR, FAILED, ACTIVE, + ) = ( + 'ERROR', 'FAILED', 'ACTIVE', + ) + + BAD_STATUSES = (ERROR, FAILED) + PROPERTIES = ( NAME, FLAVOR, SIZE, DATABASES, USERS, AVAILABILITY_ZONE, RESTORE_POINT, @@ -59,7 +69,6 @@ class OSDBInstance(resource.Resource): NAME: properties.Schema( properties.Schema.STRING, _('Name of the DB instance to create.'), - required=True, constraints=[ constraints.Length(max=255), ] @@ -153,7 +162,10 @@ class OSDBInstance(resource.Resource): schema=properties.Schema( properties.Schema.STRING, ), - required=True + required=True, + constraints=[ + constraints.Length(min=1), + ] ), }, ) @@ -190,18 +202,17 @@ class OSDBInstance(resource.Resource): return self._dbinstance - def physical_resource_name(self): + def _dbinstance_name(self): name = self.properties.get(self.NAME) if name: return name - return super(OSDBInstance, self).physical_resource_name() + return self.physical_resource_name() def handle_create(self): ''' Create cloud database instance. ''' - self.dbinstancename = self.physical_resource_name() self.flavor = nova_utils.get_flavor_id(self.trove(), self.properties[self.FLAVOR]) self.volume = {'size': self.properties[self.SIZE]} @@ -218,7 +229,7 @@ class OSDBInstance(resource.Resource): # create db instance instance = self.trove().instances.create( - self.dbinstancename, + self._dbinstance_name(), self.flavor, volume=self.volume, databases=self.databases, @@ -243,17 +254,16 @@ class OSDBInstance(resource.Resource): ''' Check if cloud DB instance creation is complete. ''' - self._refresh_instance(instance) - - if instance.status == 'ERROR': + self._refresh_instance(instance) # get updated attributes + if instance.status in self.BAD_STATUSES: raise exception.Error(_("Database instance creation failed.")) - if instance.status != 'ACTIVE': + if instance.status != self.ACTIVE: return False msg = _("Database instance %(database)s created (flavor:%(flavor)s, " "volume:%(volume)s)") - LOG.info(msg % ({'database': self.dbinstancename, + LOG.info(msg % ({'database': self._dbinstance_name(), 'flavor': self.flavor, 'volume': self.volume})) return True @@ -268,7 +278,7 @@ class OSDBInstance(resource.Resource): instance = None try: instance = self.trove().instances.get(self.resource_id) - except troveclient.exceptions.NotFound: + except troveexc.NotFound: LOG.debug("Database instance %s not found." % self.resource_id) self.resource_id_set(None) else: @@ -277,14 +287,15 @@ class OSDBInstance(resource.Resource): def check_delete_complete(self, instance): ''' - Check for completion of cloud DB instance delettion + Check for completion of cloud DB instance deletion ''' if not instance: return True try: + # For some time trove instance may continue to live self._refresh_instance(instance) - except troveclient.exceptions.NotFound: + except troveexc.NotFound: self.resource_id_set(None) return True @@ -305,23 +316,19 @@ class OSDBInstance(resource.Resource): databases = self.properties.get(self.DATABASES) if not databases: - msg = _('Databases property is required if users property' - ' is provided') + msg = _('Databases property is required if users property ' + 'is provided for resource %s.') % self.name raise exception.StackValidationFailed(message=msg) db_names = set([db[self.DATABASE_NAME] for db in databases]) for user in users: - if not user.get(self.USER_DATABASES, []): - msg = _('Must provide access to at least one database for ' - 'user %s') % user[self.USER_NAME] - raise exception.StackValidationFailed(message=msg) - missing_db = [db_name for db_name in user[self.USER_DATABASES] if db_name not in db_names] if missing_db: - msg = _('Database %s specified for user does not exist in ' - 'databases.') % missing_db + msg = (_('Database %(dbs)s specified for user does ' + 'not exist in databases for resource %(name)s.') + % {'dbs': missing_db, 'name': self.name}) raise exception.StackValidationFailed(message=msg) def href(self): diff --git a/heat/tests/test_os_database.py b/heat/tests/test_os_database.py index 87cea660c6..a6deeeca2b 100644 --- a/heat/tests/test_os_database.py +++ b/heat/tests/test_os_database.py @@ -16,7 +16,6 @@ import uuid from heat.common import exception from heat.common import template_format from heat.engine.clients import troveclient -from heat.engine import environment from heat.engine import parser from heat.engine.resources import os_database from heat.engine import scheduler @@ -24,27 +23,10 @@ from heat.tests.common import HeatTestCase from heat.tests import utils -wp_template = ''' +db_template = ''' { "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "MySQL instance running on openstack DBaaS cloud", - "Parameters" : { - "flavor": { - "Description" : "Flavor reference", - "Type": "String", - "Default": '1GB' - }, - "size": { - "Description" : "The volume size", - "Type": "Number", - "Default": '30' - }, - "name": { - "Description" : "The database instance name", - "Type": "String", - "Default": "OpenstackDbaas" - } - }, "Resources" : { "MySqlCloudDB": { "Type": "OS::Trove::Instance", @@ -91,14 +73,12 @@ class OSDBInstanceTest(HeatTestCase): super(OSDBInstanceTest, self).setUp() self.fc = self.m.CreateMockAnything() - def _setup_test_clouddbinstance(self, name, parsed_t): + def _setup_test_clouddbinstance(self, name, t): stack_name = '%s_stack' % name - t = parsed_t template = parser.Template(t) stack = parser.Stack(utils.dummy_context(), stack_name, template, - environment.Environment({'name': 'test'}), stack_id=str(uuid.uuid4())) instance = os_database.OSDBInstance( @@ -131,7 +111,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_create(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_create', t) self._stubout_create(instance, fake_dbinstance) scheduler.TaskRunner(instance.create)() @@ -140,7 +120,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_create_overlimit(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_create', t) self._stubout_create(instance, fake_dbinstance) @@ -159,7 +139,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_create_fails(self): fake_dbinstance = FakeDBInstance() fake_dbinstance.status = 'ERROR' - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_create', t) self._stubout_create(instance, fake_dbinstance) self.assertRaises(exception.ResourceFailure, @@ -168,7 +148,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_delete(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_del', t) self._stubout_create(instance, fake_dbinstance) scheduler.TaskRunner(instance.create)() @@ -187,7 +167,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_delete_overlimit(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_del', t) self._stubout_create(instance, fake_dbinstance) scheduler.TaskRunner(instance.create)() @@ -210,7 +190,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_delete_resource_none(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_del', t) self._stubout_create(instance, fake_dbinstance) scheduler.TaskRunner(instance.create)() @@ -223,7 +203,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_resource_not_found(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_del', t) self._stubout_create(instance, fake_dbinstance) scheduler.TaskRunner(instance.create)() @@ -237,7 +217,7 @@ class OSDBInstanceTest(HeatTestCase): self.m.VerifyAll() def test_osdatabase_invalid_attribute(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance("db_invalid_attrib", t) attrib = instance._resolve_attribute("invalid_attrib") self.assertIsNone(attrib) @@ -245,7 +225,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_get_hostname(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance.resource_id = 12345 self.m.StubOutWithMock(instance, 'trove') @@ -260,7 +240,7 @@ class OSDBInstanceTest(HeatTestCase): def test_osdatabase_get_href(self): fake_dbinstance = FakeDBInstance() - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance.resource_id = 12345 self.m.StubOutWithMock(instance, 'trove') @@ -271,11 +251,12 @@ class OSDBInstanceTest(HeatTestCase): self.m.ReplayAll() attrib = instance._resolve_attribute('href') self.assertEqual(fake_dbinstance.links[0]['href'], attrib) + self.m.VerifyAll() def test_osdatabase_get_href_links_none(self): fake_dbinstance = FakeDBInstance() fake_dbinstance.links = None - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance.resource_id = 12345 self.m.StubOutWithMock(instance, 'trove') @@ -286,15 +267,17 @@ class OSDBInstanceTest(HeatTestCase): self.m.ReplayAll() attrib = instance._resolve_attribute('href') self.assertIsNone(attrib) + self.m.VerifyAll() def test_osdatabase_prop_validation_success(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) instance = self._setup_test_clouddbinstance('dbinstance_test', t) ret = instance.validate() self.assertIsNone(ret) + self.m.VerifyAll() def test_osdatabase_prop_validation_invaliddb(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) t['Resources']['MySqlCloudDB']['Properties']['databases'] = [ {"name": "onedb"}] t['Resources']['MySqlCloudDB']['Properties']['users'] = [ @@ -303,16 +286,18 @@ class OSDBInstanceTest(HeatTestCase): "databases": ["invaliddb"]}] instance = self._setup_test_clouddbinstance('dbinstance_test', t) self.assertRaises(exception.StackValidationFailed, instance.validate) + self.m.VerifyAll() def test_osdatabase_prop_validation_users_none(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) t['Resources']['MySqlCloudDB']['Properties']['users'] = [] instance = self._setup_test_clouddbinstance('dbinstance_test', t) ret = instance.validate() self.assertIsNone(ret) + self.m.VerifyAll() def test_osdatabase_prop_validation_databases_none(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) t['Resources']['MySqlCloudDB']['Properties']['databases'] = [] t['Resources']['MySqlCloudDB']['Properties']['users'] = [ {"name": "testuser", @@ -320,9 +305,10 @@ class OSDBInstanceTest(HeatTestCase): "databases": ["invaliddb"]}] instance = self._setup_test_clouddbinstance('dbinstance_test', t) self.assertRaises(exception.StackValidationFailed, instance.validate) + self.m.VerifyAll() def test_osdatabase_prop_validation_user_no_db(self): - t = template_format.parse(wp_template) + t = template_format.parse(db_template) t['Resources']['MySqlCloudDB']['Properties']['databases'] = [ {"name": "validdb"}] t['Resources']['MySqlCloudDB']['Properties']['users'] = [ @@ -330,3 +316,4 @@ class OSDBInstanceTest(HeatTestCase): instance = self._setup_test_clouddbinstance('dbinstance_test', t) self.assertRaises(exception.StackValidationFailed, instance.validate) + self.m.VerifyAll()