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:
parent
3f80c2bb8e
commit
a4e7229986
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue