Merge "Improve OS::Trove::Instance resource"

This commit is contained in:
Jenkins 2014-07-08 10:41:58 +00:00 committed by Gerrit Code Review
commit 755bdb379b
2 changed files with 55 additions and 61 deletions

View File

@ -11,6 +11,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from troveclient.openstack.common.apiclient import exceptions as troveexc
from heat.common import exception from heat.common import exception
from heat.engine import attributes from heat.engine import attributes
from heat.engine.clients import troveclient from heat.engine.clients import troveclient
@ -29,6 +31,14 @@ class OSDBInstance(resource.Resource):
OpenStack cloud database instance resource. OpenStack cloud database instance resource.
''' '''
TROVE_STATUS = (
ERROR, FAILED, ACTIVE,
) = (
'ERROR', 'FAILED', 'ACTIVE',
)
BAD_STATUSES = (ERROR, FAILED)
PROPERTIES = ( PROPERTIES = (
NAME, FLAVOR, SIZE, DATABASES, USERS, AVAILABILITY_ZONE, NAME, FLAVOR, SIZE, DATABASES, USERS, AVAILABILITY_ZONE,
RESTORE_POINT, RESTORE_POINT,
@ -59,7 +69,6 @@ class OSDBInstance(resource.Resource):
NAME: properties.Schema( NAME: properties.Schema(
properties.Schema.STRING, properties.Schema.STRING,
_('Name of the DB instance to create.'), _('Name of the DB instance to create.'),
required=True,
constraints=[ constraints=[
constraints.Length(max=255), constraints.Length(max=255),
] ]
@ -153,7 +162,10 @@ class OSDBInstance(resource.Resource):
schema=properties.Schema( schema=properties.Schema(
properties.Schema.STRING, properties.Schema.STRING,
), ),
required=True required=True,
constraints=[
constraints.Length(min=1),
]
), ),
}, },
) )
@ -190,18 +202,17 @@ class OSDBInstance(resource.Resource):
return self._dbinstance return self._dbinstance
def physical_resource_name(self): def _dbinstance_name(self):
name = self.properties.get(self.NAME) name = self.properties.get(self.NAME)
if name: if name:
return name return name
return super(OSDBInstance, self).physical_resource_name() return self.physical_resource_name()
def handle_create(self): def handle_create(self):
''' '''
Create cloud database instance. Create cloud database instance.
''' '''
self.dbinstancename = self.physical_resource_name()
self.flavor = nova_utils.get_flavor_id(self.trove(), self.flavor = nova_utils.get_flavor_id(self.trove(),
self.properties[self.FLAVOR]) self.properties[self.FLAVOR])
self.volume = {'size': self.properties[self.SIZE]} self.volume = {'size': self.properties[self.SIZE]}
@ -218,7 +229,7 @@ class OSDBInstance(resource.Resource):
# create db instance # create db instance
instance = self.trove().instances.create( instance = self.trove().instances.create(
self.dbinstancename, self._dbinstance_name(),
self.flavor, self.flavor,
volume=self.volume, volume=self.volume,
databases=self.databases, databases=self.databases,
@ -243,17 +254,16 @@ class OSDBInstance(resource.Resource):
''' '''
Check if cloud DB instance creation is complete. Check if cloud DB instance creation is complete.
''' '''
self._refresh_instance(instance) self._refresh_instance(instance) # get updated attributes
if instance.status in self.BAD_STATUSES:
if instance.status == 'ERROR':
raise exception.Error(_("Database instance creation failed.")) raise exception.Error(_("Database instance creation failed."))
if instance.status != 'ACTIVE': if instance.status != self.ACTIVE:
return False return False
msg = _("Database instance %(database)s created (flavor:%(flavor)s, " msg = _("Database instance %(database)s created (flavor:%(flavor)s, "
"volume:%(volume)s)") "volume:%(volume)s)")
LOG.info(msg % ({'database': self.dbinstancename, LOG.info(msg % ({'database': self._dbinstance_name(),
'flavor': self.flavor, 'flavor': self.flavor,
'volume': self.volume})) 'volume': self.volume}))
return True return True
@ -268,7 +278,7 @@ class OSDBInstance(resource.Resource):
instance = None instance = None
try: try:
instance = self.trove().instances.get(self.resource_id) 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) LOG.debug("Database instance %s not found." % self.resource_id)
self.resource_id_set(None) self.resource_id_set(None)
else: else:
@ -277,14 +287,15 @@ class OSDBInstance(resource.Resource):
def check_delete_complete(self, instance): 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: if not instance:
return True return True
try: try:
# For some time trove instance may continue to live
self._refresh_instance(instance) self._refresh_instance(instance)
except troveclient.exceptions.NotFound: except troveexc.NotFound:
self.resource_id_set(None) self.resource_id_set(None)
return True return True
@ -305,23 +316,19 @@ class OSDBInstance(resource.Resource):
databases = self.properties.get(self.DATABASES) databases = self.properties.get(self.DATABASES)
if not databases: if not databases:
msg = _('Databases property is required if users property' msg = _('Databases property is required if users property '
' is provided') 'is provided for resource %s.') % self.name
raise exception.StackValidationFailed(message=msg) raise exception.StackValidationFailed(message=msg)
db_names = set([db[self.DATABASE_NAME] for db in databases]) db_names = set([db[self.DATABASE_NAME] for db in databases])
for user in users: 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] missing_db = [db_name for db_name in user[self.USER_DATABASES]
if db_name not in db_names] if db_name not in db_names]
if missing_db: if missing_db:
msg = _('Database %s specified for user does not exist in ' msg = (_('Database %(dbs)s specified for user does '
'databases.') % missing_db 'not exist in databases for resource %(name)s.')
% {'dbs': missing_db, 'name': self.name})
raise exception.StackValidationFailed(message=msg) raise exception.StackValidationFailed(message=msg)
def href(self): def href(self):

View File

@ -16,7 +16,6 @@ import uuid
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
from heat.engine.clients import troveclient from heat.engine.clients import troveclient
from heat.engine import environment
from heat.engine import parser from heat.engine import parser
from heat.engine.resources import os_database from heat.engine.resources import os_database
from heat.engine import scheduler from heat.engine import scheduler
@ -24,27 +23,10 @@ from heat.tests.common import HeatTestCase
from heat.tests import utils from heat.tests import utils
wp_template = ''' db_template = '''
{ {
"AWSTemplateFormatVersion" : "2010-09-09", "AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "MySQL instance running on openstack DBaaS cloud", "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" : { "Resources" : {
"MySqlCloudDB": { "MySqlCloudDB": {
"Type": "OS::Trove::Instance", "Type": "OS::Trove::Instance",
@ -91,14 +73,12 @@ class OSDBInstanceTest(HeatTestCase):
super(OSDBInstanceTest, self).setUp() super(OSDBInstanceTest, self).setUp()
self.fc = self.m.CreateMockAnything() 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 stack_name = '%s_stack' % name
t = parsed_t
template = parser.Template(t) template = parser.Template(t)
stack = parser.Stack(utils.dummy_context(), stack = parser.Stack(utils.dummy_context(),
stack_name, stack_name,
template, template,
environment.Environment({'name': 'test'}),
stack_id=str(uuid.uuid4())) stack_id=str(uuid.uuid4()))
instance = os_database.OSDBInstance( instance = os_database.OSDBInstance(
@ -131,7 +111,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_create(self): def test_osdatabase_create(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_create', t) instance = self._setup_test_clouddbinstance('dbinstance_create', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
scheduler.TaskRunner(instance.create)() scheduler.TaskRunner(instance.create)()
@ -140,7 +120,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_create_overlimit(self): def test_osdatabase_create_overlimit(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_create', t) instance = self._setup_test_clouddbinstance('dbinstance_create', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
@ -159,7 +139,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_create_fails(self): def test_osdatabase_create_fails(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
fake_dbinstance.status = 'ERROR' fake_dbinstance.status = 'ERROR'
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_create', t) instance = self._setup_test_clouddbinstance('dbinstance_create', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
self.assertRaises(exception.ResourceFailure, self.assertRaises(exception.ResourceFailure,
@ -168,7 +148,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_delete(self): def test_osdatabase_delete(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_del', t) instance = self._setup_test_clouddbinstance('dbinstance_del', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
scheduler.TaskRunner(instance.create)() scheduler.TaskRunner(instance.create)()
@ -187,7 +167,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_delete_overlimit(self): def test_osdatabase_delete_overlimit(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_del', t) instance = self._setup_test_clouddbinstance('dbinstance_del', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
scheduler.TaskRunner(instance.create)() scheduler.TaskRunner(instance.create)()
@ -210,7 +190,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_delete_resource_none(self): def test_osdatabase_delete_resource_none(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_del', t) instance = self._setup_test_clouddbinstance('dbinstance_del', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
scheduler.TaskRunner(instance.create)() scheduler.TaskRunner(instance.create)()
@ -223,7 +203,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_resource_not_found(self): def test_osdatabase_resource_not_found(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_del', t) instance = self._setup_test_clouddbinstance('dbinstance_del', t)
self._stubout_create(instance, fake_dbinstance) self._stubout_create(instance, fake_dbinstance)
scheduler.TaskRunner(instance.create)() scheduler.TaskRunner(instance.create)()
@ -237,7 +217,7 @@ class OSDBInstanceTest(HeatTestCase):
self.m.VerifyAll() self.m.VerifyAll()
def test_osdatabase_invalid_attribute(self): 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) instance = self._setup_test_clouddbinstance("db_invalid_attrib", t)
attrib = instance._resolve_attribute("invalid_attrib") attrib = instance._resolve_attribute("invalid_attrib")
self.assertIsNone(attrib) self.assertIsNone(attrib)
@ -245,7 +225,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_get_hostname(self): def test_osdatabase_get_hostname(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345 instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove') self.m.StubOutWithMock(instance, 'trove')
@ -260,7 +240,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_get_href(self): def test_osdatabase_get_href(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
t = template_format.parse(wp_template) t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345 instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove') self.m.StubOutWithMock(instance, 'trove')
@ -271,11 +251,12 @@ class OSDBInstanceTest(HeatTestCase):
self.m.ReplayAll() self.m.ReplayAll()
attrib = instance._resolve_attribute('href') attrib = instance._resolve_attribute('href')
self.assertEqual(fake_dbinstance.links[0]['href'], attrib) self.assertEqual(fake_dbinstance.links[0]['href'], attrib)
self.m.VerifyAll()
def test_osdatabase_get_href_links_none(self): def test_osdatabase_get_href_links_none(self):
fake_dbinstance = FakeDBInstance() fake_dbinstance = FakeDBInstance()
fake_dbinstance.links = None 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 = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345 instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove') self.m.StubOutWithMock(instance, 'trove')
@ -286,15 +267,17 @@ class OSDBInstanceTest(HeatTestCase):
self.m.ReplayAll() self.m.ReplayAll()
attrib = instance._resolve_attribute('href') attrib = instance._resolve_attribute('href')
self.assertIsNone(attrib) self.assertIsNone(attrib)
self.m.VerifyAll()
def test_osdatabase_prop_validation_success(self): 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) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
ret = instance.validate() ret = instance.validate()
self.assertIsNone(ret) self.assertIsNone(ret)
self.m.VerifyAll()
def test_osdatabase_prop_validation_invaliddb(self): def test_osdatabase_prop_validation_invaliddb(self):
t = template_format.parse(wp_template) t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties']['databases'] = [ t['Resources']['MySqlCloudDB']['Properties']['databases'] = [
{"name": "onedb"}] {"name": "onedb"}]
t['Resources']['MySqlCloudDB']['Properties']['users'] = [ t['Resources']['MySqlCloudDB']['Properties']['users'] = [
@ -303,16 +286,18 @@ class OSDBInstanceTest(HeatTestCase):
"databases": ["invaliddb"]}] "databases": ["invaliddb"]}]
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.assertRaises(exception.StackValidationFailed, instance.validate) self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()
def test_osdatabase_prop_validation_users_none(self): 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'] = [] t['Resources']['MySqlCloudDB']['Properties']['users'] = []
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
ret = instance.validate() ret = instance.validate()
self.assertIsNone(ret) self.assertIsNone(ret)
self.m.VerifyAll()
def test_osdatabase_prop_validation_databases_none(self): 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']['databases'] = []
t['Resources']['MySqlCloudDB']['Properties']['users'] = [ t['Resources']['MySqlCloudDB']['Properties']['users'] = [
{"name": "testuser", {"name": "testuser",
@ -320,9 +305,10 @@ class OSDBInstanceTest(HeatTestCase):
"databases": ["invaliddb"]}] "databases": ["invaliddb"]}]
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.assertRaises(exception.StackValidationFailed, instance.validate) self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()
def test_osdatabase_prop_validation_user_no_db(self): 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'] = [ t['Resources']['MySqlCloudDB']['Properties']['databases'] = [
{"name": "validdb"}] {"name": "validdb"}]
t['Resources']['MySqlCloudDB']['Properties']['users'] = [ t['Resources']['MySqlCloudDB']['Properties']['users'] = [
@ -330,3 +316,4 @@ class OSDBInstanceTest(HeatTestCase):
instance = self._setup_test_clouddbinstance('dbinstance_test', t) instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.assertRaises(exception.StackValidationFailed, instance.validate) self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()