Add datastore properties to OS::Trove::Instance

Latest Trove supports datastore type and datastore version options
when creating Trove instances. This patch enables their usage in Heat.

Change-Id: I1d037a9fbf66f13c3195a55c7325d19b5e9a058c
Closes-bug: #1257690
This commit is contained in:
Pavlo Shchelokovskyy 2014-06-03 13:23:38 +03:00
parent 3f80c2bb8e
commit a4e7229986
2 changed files with 152 additions and 9 deletions

View File

@ -41,10 +41,10 @@ class OSDBInstance(resource.Resource):
PROPERTIES = (
NAME, FLAVOR, SIZE, DATABASES, USERS, AVAILABILITY_ZONE,
RESTORE_POINT,
RESTORE_POINT, DATASTORE_TYPE, DATASTORE_VERSION,
) = (
'name', 'flavor', 'size', 'databases', 'users', 'availability_zone',
'restore_point',
'restore_point', 'datastore_type', 'datastore_version',
)
_DATABASE_KEYS = (
@ -78,6 +78,22 @@ class OSDBInstance(resource.Resource):
_('Reference to a flavor for creating DB instance.'),
required=True
),
DATASTORE_TYPE: properties.Schema(
properties.Schema.STRING,
_("Name of registered datastore type."),
constraints=[
constraints.Length(max=255)
]
),
DATASTORE_VERSION: properties.Schema(
properties.Schema.STRING,
_("Name of the registered datastore version. "
"It must exist for provided datastore type. "
"Defaults to using single active version. "
"If several active versions exist for provided datastore type, "
"explicit value for this parameter must be specified."),
constraints=[constraints.Length(max=255)]
),
SIZE: properties.Schema(
properties.Schema.INTEGER,
_('Database volume size in GB.'),
@ -220,6 +236,8 @@ class OSDBInstance(resource.Resource):
self.users = self.properties.get(self.USERS)
restore_point = self.properties.get(self.RESTORE_POINT)
zone = self.properties.get(self.AVAILABILITY_ZONE)
self.datastore_type = self.properties.get(self.DATASTORE_TYPE)
self.datastore_version = self.properties.get(self.DATASTORE_VERSION)
# convert user databases to format required for troveclient.
# that is, list of database dictionaries
@ -235,7 +253,9 @@ class OSDBInstance(resource.Resource):
databases=self.databases,
users=self.users,
restorePoint=restore_point,
availability_zone=zone)
availability_zone=zone,
datastore=self.datastore_type,
datastore_version=self.datastore_version)
self.resource_id_set(instance.id)
return instance
@ -262,10 +282,14 @@ class OSDBInstance(resource.Resource):
return False
msg = _("Database instance %(database)s created (flavor:%(flavor)s, "
"volume:%(volume)s)")
LOG.info(msg % ({'database': self._dbinstance_name(),
'flavor': self.flavor,
'volume': self.volume}))
"volume:%(volume)s, datastore:%(datastore_type)s, "
"datastore_version:%(datastore_version)s)")
LOG.info(msg % {'database': self._dbinstance_name(),
'flavor': self.flavor,
'volume': self.volume,
'datastore_type': self.datastore_type,
'datastore_version': self.datastore_version})
return True
def handle_delete(self):
@ -309,6 +333,39 @@ class OSDBInstance(resource.Resource):
if res:
return res
datastore_type = self.properties.get(self.DATASTORE_TYPE)
datastore_version = self.properties.get(self.DATASTORE_VERSION)
if datastore_type:
# get current active versions
allowed_versions = self.trove().datastore_versions.list(
datastore_type)
allowed_version_names = [v.name for v in allowed_versions]
if datastore_version:
if datastore_version not in allowed_version_names:
msg = _("Datastore version %(dsversion)s "
"for datastore type %(dstype)s is not valid. "
"Allowed versions are %(allowed)s.") % {
'dstype': datastore_type,
'dsversion': datastore_version,
'allowed': ', '.join(allowed_version_names)}
raise exception.StackValidationFailed(message=msg)
else:
if len(allowed_versions) > 1:
msg = _("Multiple active datastore versions exist for "
"datastore type %(dstype)s. "
"Explicit datastore version must be provided. "
"Allowed versions are %(allowed)s.") % {
'dstype': datastore_type,
'allowed': ', '.join(allowed_version_names)}
raise exception.StackValidationFailed(message=msg)
else:
if datastore_version:
msg = _("Not allowed - %(dsver)s without %(dstype)s.") % {
'dsver': self.DATASTORE_VERSION,
'dstype': self.DATASTORE_TYPE}
raise exception.StackValidationFailed(message=msg)
# check validity of user and databases
users = self.properties.get(self.USERS)
if not users:

View File

@ -13,6 +13,8 @@
import uuid
import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients import troveclient
@ -36,7 +38,9 @@ db_template = '''
"size" : 30,
"users" : [{"name": "testuser", "password": "pass", "databases":
["validdb"]}],
"databases" : [{"name": "validdb"}]
"databases" : [{"name": "validdb"}],
"datastore_type": "SomeDStype",
"datastore_version": "MariaDB-5.5"
}
}
}
@ -68,6 +72,11 @@ class FakeFlavor(object):
self.name = name
class FakeVersion(object):
def __init__(self, name="MariaDB-5.5"):
self.name = name
class OSDBInstanceTest(HeatTestCase):
def setUp(self):
super(OSDBInstanceTest, self).setUp()
@ -105,10 +114,21 @@ class OSDBInstanceTest(HeatTestCase):
databases=databases,
users=users,
restorePoint=None,
availability_zone=None
availability_zone=None,
datastore="SomeDStype",
datastore_version="MariaDB-5.5"
).AndReturn(fake_dbinstance)
self.m.ReplayAll()
def _stubout_validate(self, instance):
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'datastore_versions')
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
self.fc.datastore_versions.list(instance.properties['datastore_type']
).AndReturn([FakeVersion()])
self.m.ReplayAll()
def test_osdatabase_create(self):
fake_dbinstance = FakeDBInstance()
t = template_format.parse(db_template)
@ -272,6 +292,7 @@ class OSDBInstanceTest(HeatTestCase):
def test_osdatabase_prop_validation_success(self):
t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
ret = instance.validate()
self.assertIsNone(ret)
self.m.VerifyAll()
@ -285,6 +306,7 @@ class OSDBInstanceTest(HeatTestCase):
"password": "pass",
"databases": ["invaliddb"]}]
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()
@ -292,6 +314,7 @@ class OSDBInstanceTest(HeatTestCase):
t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties']['users'] = []
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
ret = instance.validate()
self.assertIsNone(ret)
self.m.VerifyAll()
@ -304,6 +327,7 @@ class OSDBInstanceTest(HeatTestCase):
"password": "pass",
"databases": ["invaliddb"]}]
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()
@ -317,3 +341,65 @@ class OSDBInstanceTest(HeatTestCase):
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.assertRaises(exception.StackValidationFailed, instance.validate)
self.m.VerifyAll()
def test_osdatabase_prop_validation_no_datastore_yes_version(self):
t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties'].pop('datastore_type')
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
ex = self.assertRaises(exception.StackValidationFailed,
instance.validate)
exp_msg = "Not allowed - datastore_version without datastore_type."
self.assertEqual(exp_msg, six.text_type(ex))
self.m.VerifyAll()
def test_osdatabase_prop_validation_no_dsversion(self):
t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties'][
'datastore_type'] = 'mysql'
t['Resources']['MySqlCloudDB']['Properties'].pop('datastore_version')
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
self.assertIsNone(instance.validate())
self.m.VerifyAll()
def test_osdatabase_prop_validation_wrong_dsversion(self):
t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties'][
'datastore_type'] = 'mysql'
t['Resources']['MySqlCloudDB']['Properties'][
'datastore_version'] = 'SomeVersion'
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self._stubout_validate(instance)
ex = self.assertRaises(exception.StackValidationFailed,
instance.validate)
expected_msg = ("Datastore version SomeVersion for datastore type "
"mysql is not valid. "
"Allowed versions are MariaDB-5.5.")
self.assertEqual(expected_msg, six.text_type(ex))
self.m.VerifyAll()
def test_osdatabase_prop_validation_implicit_version_fail(self):
t = template_format.parse(db_template)
t['Resources']['MySqlCloudDB']['Properties'][
'datastore_type'] = 'mysql'
t['Resources']['MySqlCloudDB']['Properties'].pop('datastore_version')
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'datastore_versions')
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
self.fc.datastore_versions.list(
instance.properties['datastore_type']
).AndReturn([FakeVersion(), FakeVersion('MariaDB-5.0')])
self.m.ReplayAll()
ex = self.assertRaises(exception.StackValidationFailed,
instance.validate)
expected_msg = ("Multiple active datastore versions exist for "
"datastore type mysql. "
"Explicit datastore version must be provided. "
"Allowed versions are MariaDB-5.5, MariaDB-5.0.")
self.assertEqual(expected_msg, six.text_type(ex))
self.m.VerifyAll()