Datastore Configuration Parameters stored in db
Changes: Add datastore configuration paramters to configuration model Add ability to CRUD configuration parameters adding a manage cmd to import the config params partially implements blueprint configuration-parameters-in-db Change-Id: I51d5281ba8922dc19c74e7c6e972f40e45416d50
This commit is contained in:
committed by
Nikhil Manchanda
parent
74b436e1d2
commit
9a35fa8b4d
@@ -24,6 +24,7 @@ gettext.install('trove', unicode=1)
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import utils
|
||||
from trove.configuration import models as config_models
|
||||
from trove.db import get_db_api
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.datastore import models as datastore_models
|
||||
@@ -80,6 +81,14 @@ class Commands(object):
|
||||
self.db_api.drop_db(CONF)
|
||||
self.db_sync(repo_path)
|
||||
|
||||
def db_load_datastore_config_parameters(self,
|
||||
datastore_version_id,
|
||||
config_file_location):
|
||||
print("Loading config parameters for datastore version: %s"
|
||||
% datastore_version_id)
|
||||
config_models.load_datastore_configuration_parameters(
|
||||
datastore_version_id, config_file_location)
|
||||
|
||||
def params_of(self, command_name):
|
||||
if Commands.has(command_name):
|
||||
return utils.MethodInspector(getattr(self, command_name))
|
||||
@@ -142,6 +151,19 @@ def main():
|
||||
parser = subparser.add_parser(
|
||||
'db_recreate', description='Drop the database and recreate it.')
|
||||
parser.add_argument('repo_path', help=repo_path_help)
|
||||
|
||||
parser = subparser.add_parser(
|
||||
'db_load_datastore_config_parameters',
|
||||
description='Loads configuration group parameter validation rules '
|
||||
'for a datastore version into the database.')
|
||||
parser.add_argument(
|
||||
'datastore_version_id',
|
||||
help='UUID of the datastore version.')
|
||||
parser.add_argument(
|
||||
'config_file_location',
|
||||
help='Fully qualified file path to the configuration group '
|
||||
'parameter validation rules.')
|
||||
|
||||
cfg.custom_parser('action', actions)
|
||||
cfg.parse_args(sys.argv)
|
||||
|
||||
|
||||
@@ -24,6 +24,31 @@ url_ref = {
|
||||
'|(?:%[0-9a-fA-F][0-9a-fA-F]))+'
|
||||
}
|
||||
|
||||
boolean_string = {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
}
|
||||
|
||||
configuration_data_types = {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"pattern": "integer|string"
|
||||
}
|
||||
|
||||
configuration_integer_size = {
|
||||
"type": "string",
|
||||
"maxLength": 40,
|
||||
"pattern": "[0-9]+"
|
||||
}
|
||||
|
||||
configuration_non_empty_string = {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^.*[0-9a-zA-Z]+.*$"
|
||||
}
|
||||
|
||||
flavorref = {
|
||||
'oneOf': [
|
||||
url_ref,
|
||||
@@ -502,6 +527,45 @@ configuration = {
|
||||
}
|
||||
}
|
||||
|
||||
mgmt_configuration = {
|
||||
"create": {
|
||||
"name": "configuration_parameter:create",
|
||||
"type": "object",
|
||||
"required": ["configuration-parameter"],
|
||||
"properties": {
|
||||
"configuration-parameter": {
|
||||
"type": "object",
|
||||
"required": ["name", "restart_required", "data_type"],
|
||||
"properties": {
|
||||
"name": configuration_non_empty_string,
|
||||
"data_type": configuration_data_types,
|
||||
"restart_required": boolean_string,
|
||||
"max_size": configuration_integer_size,
|
||||
"min_size": configuration_integer_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"name": "configuration_parameter:update",
|
||||
"type": "object",
|
||||
"required": ["configuration-parameter"],
|
||||
"properties": {
|
||||
"configuration-parameter": {
|
||||
"type": "object",
|
||||
"required": ["name", "restart_required", "data_type"],
|
||||
"properties": {
|
||||
"name": configuration_non_empty_string,
|
||||
"data_type": configuration_data_types,
|
||||
"restart_required": boolean_string,
|
||||
"max_size": configuration_integer_size,
|
||||
"min_size": configuration_integer_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
account = {
|
||||
'create': {
|
||||
"type": "object",
|
||||
|
||||
@@ -14,48 +14,9 @@
|
||||
# under the License.
|
||||
|
||||
import io
|
||||
import json
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import utils
|
||||
from trove.openstack.common import log as logging
|
||||
from six.moves import configparser
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
ENV = utils.ENV
|
||||
|
||||
|
||||
def _get_item(key, dictList):
|
||||
for item in dictList:
|
||||
if key == item.get('name'):
|
||||
return item
|
||||
|
||||
|
||||
def do_configs_require_restart(overrides, datastore_manager='mysql'):
|
||||
rules = get_validation_rules(datastore_manager=datastore_manager)
|
||||
LOG.debug("overrides: %s" % overrides)
|
||||
LOG.debug("rules?: %s" % rules)
|
||||
for key in overrides.keys():
|
||||
rule = _get_item(key, rules['configuration-parameters'])
|
||||
if rule.get('restart_required'):
|
||||
LOG.debug("rule requires restart: %s" % rule)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_validation_rules(datastore_manager='mysql'):
|
||||
try:
|
||||
config_location = ("%s/validation-rules.json" % datastore_manager)
|
||||
template = ENV.get_template(config_location)
|
||||
return json.loads(template.render())
|
||||
except Exception:
|
||||
msg = "This operation is not supported by this datastore at this time."
|
||||
LOG.exception(msg)
|
||||
raise exception.UnprocessableEntity(message=msg)
|
||||
|
||||
|
||||
class MySQLConfParser(object):
|
||||
"""MySQLConfParser"""
|
||||
def __init__(self, config):
|
||||
|
||||
@@ -421,7 +421,12 @@ class ConfigurationDatastoreNotMatchInstance(TroveError):
|
||||
|
||||
class ConfigurationParameterDeleted(TroveError):
|
||||
message = _("%(parameter_name)s parameter can no longer be "
|
||||
" set as of %(parameter_deleted_at)s.")
|
||||
"set as of %(parameter_deleted_at)s.")
|
||||
|
||||
|
||||
class ConfigurationParameterAlreadyExists(TroveError):
|
||||
message = _("%(parameter_name)s parameter already exists "
|
||||
"for datastore version %(datastore_version)s.")
|
||||
|
||||
|
||||
class ConfigurationAlreadyAttached(TroveError):
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
from trove.common import cfg
|
||||
from trove.common import configurations
|
||||
from trove.common import exception
|
||||
from trove.common import utils
|
||||
from trove.common.exception import ModelNotFoundError
|
||||
from trove.datastore import models as dstore_models
|
||||
from trove.db import get_db_api
|
||||
from trove.db import models as dbmodels
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.openstack.common.gettextutils import _
|
||||
@@ -66,6 +69,10 @@ class Configurations(object):
|
||||
|
||||
class Configuration(object):
|
||||
|
||||
def __init__(self, context, configuration_id):
|
||||
self.context = context
|
||||
self.configuration_id = configuration_id
|
||||
|
||||
@property
|
||||
def instances(self):
|
||||
return self.instances
|
||||
@@ -89,7 +96,7 @@ class Configuration(object):
|
||||
"values: %s" % (cfg_id, values))
|
||||
config_items = []
|
||||
for key, val in values.iteritems():
|
||||
config_item = ConfigurationParameter.create(
|
||||
config_item = DBConfigurationParameter.create(
|
||||
configuration_id=cfg_id,
|
||||
configuration_key=key,
|
||||
configuration_value=val)
|
||||
@@ -106,8 +113,8 @@ class Configuration(object):
|
||||
|
||||
@staticmethod
|
||||
def remove_all_items(context, id, deleted_at):
|
||||
items = ConfigurationParameter.find_all(configuration_id=id,
|
||||
deleted=False).all()
|
||||
items = DBConfigurationParameter.find_all(configuration_id=id,
|
||||
deleted=False).all()
|
||||
LOG.debug("Removing all configuration values for %s" % id)
|
||||
for item in items:
|
||||
item.deleted = True
|
||||
@@ -125,59 +132,84 @@ class Configuration(object):
|
||||
def load(context, id):
|
||||
try:
|
||||
if context.is_admin:
|
||||
config_info = DBConfiguration.find_by(id=id,
|
||||
deleted=False)
|
||||
return DBConfiguration.find_by(id=id, deleted=False)
|
||||
else:
|
||||
config_info = DBConfiguration.find_by(id=id,
|
||||
tenant_id=context.tenant,
|
||||
deleted=False)
|
||||
return DBConfiguration.find_by(id=id,
|
||||
tenant_id=context.tenant,
|
||||
deleted=False)
|
||||
except ModelNotFoundError:
|
||||
msg = _("Configuration group with ID %s could not be found.") % id
|
||||
raise ModelNotFoundError(msg)
|
||||
return config_info
|
||||
|
||||
@staticmethod
|
||||
def find_parameter_details(name, detail_list):
|
||||
for item in detail_list:
|
||||
if item.name == name:
|
||||
return item
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def load_items(context, id):
|
||||
datastore = Configuration.load_configuration_datastore_version(context,
|
||||
id)
|
||||
config_items = ConfigurationParameter.find_all(configuration_id=id,
|
||||
deleted=False).all()
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=datastore.manager)
|
||||
datastore_v = Configuration.load_configuration_datastore_version(
|
||||
context,
|
||||
id)
|
||||
config_items = DBConfigurationParameter.find_all(
|
||||
configuration_id=id, deleted=False).all()
|
||||
|
||||
def _get_rule(key):
|
||||
for rule in rules['configuration-parameters']:
|
||||
if str(rule.get('name')) == key:
|
||||
return rule
|
||||
detail_list = DatastoreConfigurationParameters.load_parameters(
|
||||
datastore_v.id)
|
||||
|
||||
for item in config_items:
|
||||
rule = _get_rule(str(item.configuration_key))
|
||||
if rule.get('type') == 'boolean':
|
||||
rule = Configuration.find_parameter_details(
|
||||
str(item.configuration_key), detail_list)
|
||||
if not rule:
|
||||
continue
|
||||
if rule.data_type == 'boolean':
|
||||
item.configuration_value = bool(int(item.configuration_value))
|
||||
elif rule.get('type') == 'integer':
|
||||
elif rule.data_type == 'integer':
|
||||
item.configuration_value = int(item.configuration_value)
|
||||
else:
|
||||
item.configuration_value = str(item.configuration_value)
|
||||
return config_items
|
||||
|
||||
@staticmethod
|
||||
def get_configuration_overrides(context, configuration_id):
|
||||
def get_configuration_overrides(self):
|
||||
"""Gets the overrides dictionary to apply to an instance."""
|
||||
overrides = {}
|
||||
if configuration_id:
|
||||
config_items = Configuration.load_items(context,
|
||||
id=configuration_id)
|
||||
if self.configuration_id:
|
||||
config_items = Configuration.load_items(self.context,
|
||||
id=self.configuration_id)
|
||||
|
||||
for i in config_items:
|
||||
overrides[i.configuration_key] = i.configuration_value
|
||||
return overrides
|
||||
|
||||
def does_configuration_need_restart(self):
|
||||
datastore_v = Configuration.load_configuration_datastore_version(
|
||||
self.context,
|
||||
self.configuration_id)
|
||||
config_items = Configuration.load_items(self.context,
|
||||
id=self.configuration_id)
|
||||
LOG.debug("config_items: %s" % config_items)
|
||||
detail_list = DatastoreConfigurationParameters.load_parameters(
|
||||
datastore_v.id, show_deleted=True)
|
||||
|
||||
for i in config_items:
|
||||
LOG.debug("config item: %s" % i)
|
||||
details = Configuration.find_parameter_details(
|
||||
i.configuration_key, detail_list)
|
||||
LOG.debug("parameter details: %s" % details)
|
||||
if not details:
|
||||
raise exception.NotFound(uuid=i.configuration_key)
|
||||
if bool(details.restart_required):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def save(context, configuration, configuration_items, instances):
|
||||
DBConfiguration.save(configuration)
|
||||
for item in configuration_items:
|
||||
item["deleted_at"] = None
|
||||
ConfigurationParameter.save(item)
|
||||
DBConfigurationParameter.save(item)
|
||||
|
||||
items = Configuration.load_items(context, configuration.id)
|
||||
|
||||
@@ -210,7 +242,7 @@ class DBConfiguration(dbmodels.DatabaseModelBase):
|
||||
return datastore_version
|
||||
|
||||
|
||||
class ConfigurationParameter(dbmodels.DatabaseModelBase):
|
||||
class DBConfigurationParameter(dbmodels.DatabaseModelBase):
|
||||
_data_fields = ['configuration_id', 'configuration_key',
|
||||
'configuration_value', 'deleted',
|
||||
'deleted_at']
|
||||
@@ -219,8 +251,164 @@ class ConfigurationParameter(dbmodels.DatabaseModelBase):
|
||||
return self.configuration_key.__hash__()
|
||||
|
||||
|
||||
class DBDatastoreConfigurationParameters(dbmodels.DatabaseModelBase):
|
||||
"""Model for storing the configuration parameters on a datastore."""
|
||||
_auto_generated_attrs = ['id']
|
||||
_data_fields = [
|
||||
'name',
|
||||
'datastore_version_id',
|
||||
'restart_required',
|
||||
'max_size',
|
||||
'min_size',
|
||||
'data_type',
|
||||
'deleted',
|
||||
'deleted_at',
|
||||
]
|
||||
_table_name = "datastore_configuration_parameters"
|
||||
preserve_on_delete = True
|
||||
|
||||
|
||||
class DatastoreConfigurationParameters(object):
|
||||
def __init__(self, db_info):
|
||||
self.db_info = db_info
|
||||
|
||||
@staticmethod
|
||||
def create(**kwargs):
|
||||
"""Create a configuration parameter for a datastore version."""
|
||||
|
||||
# Do we already have a parameter in the db?
|
||||
# yes: and its deleted then modify the param
|
||||
# yes: and its not deleted then error on create.
|
||||
# no: then just create the new param
|
||||
ds_v_id = kwargs.get('datastore_version_id')
|
||||
config_param_name = kwargs.get('name')
|
||||
try:
|
||||
param = DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
ds_v_id,
|
||||
config_param_name,
|
||||
show_deleted=True)
|
||||
if param.deleted == 1:
|
||||
param.restart_required = kwargs.get('restart_required')
|
||||
param.data_type = kwargs.get('data_type')
|
||||
param.max_size = kwargs.get('max_size')
|
||||
param.min_size = kwargs.get('min_size')
|
||||
param.deleted = 0
|
||||
param.save()
|
||||
return param
|
||||
else:
|
||||
raise exception.ConfigurationParameterAlreadyExists(
|
||||
parameter_name=config_param_name,
|
||||
datastore_version=ds_v_id)
|
||||
except exception.NotFound:
|
||||
pass
|
||||
config_param = DBDatastoreConfigurationParameters.create(
|
||||
**kwargs)
|
||||
return config_param
|
||||
|
||||
@staticmethod
|
||||
def delete(version_id, config_param_name):
|
||||
config_param = DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
version_id, config_param_name)
|
||||
config_param.deleted = True
|
||||
config_param.deleted_at = datetime.utcnow()
|
||||
config_param.save()
|
||||
|
||||
@classmethod
|
||||
def load_parameters(cls, datastore_version_id, show_deleted=False):
|
||||
try:
|
||||
if show_deleted:
|
||||
return DBDatastoreConfigurationParameters.find_all(
|
||||
datastore_version_id=datastore_version_id
|
||||
)
|
||||
else:
|
||||
return DBDatastoreConfigurationParameters.find_all(
|
||||
datastore_version_id=datastore_version_id,
|
||||
deleted=False
|
||||
)
|
||||
except exception.NotFound:
|
||||
raise exception.NotFound(uuid=datastore_version_id)
|
||||
|
||||
@classmethod
|
||||
def load_parameter(cls, config_id, show_deleted=False):
|
||||
try:
|
||||
if show_deleted:
|
||||
return DBDatastoreConfigurationParameters.find_by(
|
||||
id=config_id
|
||||
)
|
||||
else:
|
||||
return DBDatastoreConfigurationParameters.find_by(
|
||||
id=config_id, deleted=False
|
||||
)
|
||||
except exception.NotFound:
|
||||
raise exception.NotFound(uuid=config_id)
|
||||
|
||||
@classmethod
|
||||
def load_parameter_by_name(cls, datastore_version_id, config_param_name,
|
||||
show_deleted=False):
|
||||
try:
|
||||
if show_deleted:
|
||||
return DBDatastoreConfigurationParameters.find_by(
|
||||
datastore_version_id=datastore_version_id,
|
||||
name=config_param_name
|
||||
)
|
||||
else:
|
||||
return DBDatastoreConfigurationParameters.find_by(
|
||||
datastore_version_id=datastore_version_id,
|
||||
name=config_param_name,
|
||||
deleted=False
|
||||
)
|
||||
except exception.NotFound:
|
||||
raise exception.NotFound(uuid=config_param_name)
|
||||
|
||||
|
||||
def create_or_update_datastore_configuration_parameter(name,
|
||||
datastore_version_id,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size):
|
||||
get_db_api().configure_db(CONF)
|
||||
datastore_version = dstore_models.DatastoreVersion.load_by_uuid(
|
||||
datastore_version_id)
|
||||
try:
|
||||
config = DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
datastore_version_id, name, show_deleted=True)
|
||||
config.restart_required = restart_required
|
||||
config.max_size = max_size
|
||||
config.min_size = min_size
|
||||
config.data_type = data_type
|
||||
get_db_api().save(config)
|
||||
except exception.NotFound:
|
||||
config = DBDatastoreConfigurationParameters(
|
||||
id=utils.generate_uuid(),
|
||||
name=name,
|
||||
datastore_version_id=datastore_version.id,
|
||||
restart_required=restart_required,
|
||||
data_type=data_type,
|
||||
max_size=max_size,
|
||||
min_size=min_size,
|
||||
deleted=False,
|
||||
)
|
||||
get_db_api().save(config)
|
||||
|
||||
|
||||
def load_datastore_configuration_parameters(datastore_version_id, config_file):
|
||||
with open(config_file) as f:
|
||||
config = json.load(f)
|
||||
for param in config['configuration-parameters']:
|
||||
create_or_update_datastore_configuration_parameter(
|
||||
param['name'],
|
||||
datastore_version_id,
|
||||
param['restart_required'],
|
||||
param['type'],
|
||||
param.get('max'),
|
||||
param.get('min'),
|
||||
)
|
||||
|
||||
|
||||
def persisted_models():
|
||||
return {
|
||||
'configurations': DBConfiguration,
|
||||
'configuration_parameters': ConfigurationParameter
|
||||
'configuration_parameters': DBConfigurationParameter,
|
||||
'datastore_configuration_parameters': DBDatastoreConfigurationParameters, # noqa
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
|
||||
from datetime import datetime
|
||||
from trove.common import cfg
|
||||
from trove.common import configurations
|
||||
from trove.common import exception
|
||||
from trove.common import pagination
|
||||
from trove.common import wsgi
|
||||
from trove.configuration import models
|
||||
from trove.configuration import views
|
||||
from trove.configuration.models import ConfigurationParameter
|
||||
from trove.configuration.models import DBConfigurationParameter
|
||||
from trove.configuration.models import DatastoreConfigurationParameters
|
||||
from trove.datastore import models as ds_models
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.openstack.common.gettextutils import _
|
||||
@@ -101,10 +101,10 @@ class ConfigurationsController(wsgi.Controller):
|
||||
# validate that the values passed in are permitted by the operator.
|
||||
ConfigurationsController._validate_configuration(
|
||||
body['configuration']['values'],
|
||||
datastore_manager=datastore_version.manager)
|
||||
datastore_version=datastore_version)
|
||||
|
||||
for k, v in values.iteritems():
|
||||
configItems.append(ConfigurationParameter(
|
||||
configItems.append(DBConfigurationParameter(
|
||||
configuration_key=k,
|
||||
configuration_value=v))
|
||||
|
||||
@@ -180,47 +180,42 @@ class ConfigurationsController(wsgi.Controller):
|
||||
if 'values' in configuration:
|
||||
# validate that the values passed in are permitted by the operator.
|
||||
ConfigurationsController._validate_configuration(
|
||||
configuration['values'], datastore_manager=ds_version.manager)
|
||||
configuration['values'], datastore_version=ds_version)
|
||||
for k, v in configuration['values'].iteritems():
|
||||
items.append(ConfigurationParameter(configuration_id=group.id,
|
||||
configuration_key=k,
|
||||
configuration_value=v,
|
||||
deleted=False))
|
||||
items.append(DBConfigurationParameter(
|
||||
configuration_id=group.id,
|
||||
configuration_key=k,
|
||||
configuration_value=v,
|
||||
deleted=False))
|
||||
return items
|
||||
|
||||
@staticmethod
|
||||
def _validate_configuration(values, datastore_manager=None):
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=datastore_manager)
|
||||
|
||||
LOG.debug("Validating configuration values for group "
|
||||
"with manager %s" % datastore_manager)
|
||||
def _validate_configuration(values, datastore_version=None):
|
||||
LOG.info(_("Validating configuration values"))
|
||||
for k, v in values.iteritems():
|
||||
# get the validation rule dictionary, which will ensure there is a
|
||||
# rule for the given key name. An exception will be thrown if no
|
||||
# valid rule is located.
|
||||
rule = ConfigurationsController._get_item(
|
||||
k, rules['configuration-parameters'])
|
||||
rule = DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
datastore_version.id, k)
|
||||
|
||||
if rule.get('deleted_at'):
|
||||
raise exception.ConfigurationParameterDeleted(
|
||||
parameter_name=rule.get('name'),
|
||||
parameter_deleted_at=rule.get('deleted_at'))
|
||||
if not rule or rule.deleted:
|
||||
output = {"key": k}
|
||||
msg = _("The parameter provided for the configuration "
|
||||
"%(key)s is not available.") % output
|
||||
raise exception.UnprocessableEntity(message=msg)
|
||||
|
||||
# type checking
|
||||
valueType = rule.get('type')
|
||||
value_type = rule.data_type
|
||||
|
||||
if not isinstance(v, ConfigurationsController._find_type(
|
||||
valueType)):
|
||||
output = {"key": k, "type": valueType}
|
||||
value_type)):
|
||||
output = {"key": k, "type": value_type}
|
||||
msg = _("The value provided for the configuration "
|
||||
"parameter %(key)s is not of type %(type)s.") % output
|
||||
raise exception.UnprocessableEntity(message=msg)
|
||||
|
||||
# integer min/max checking
|
||||
if isinstance(v, int) and not isinstance(v, bool):
|
||||
if isinstance(v, (int, long)) and not isinstance(v, bool):
|
||||
try:
|
||||
min_value = int(rule.get('min'))
|
||||
min_value = int(rule.min_size)
|
||||
except ValueError:
|
||||
raise exception.TroveError(_(
|
||||
"Invalid or unsupported min value defined in the "
|
||||
@@ -234,7 +229,7 @@ class ConfigurationsController(wsgi.Controller):
|
||||
raise exception.UnprocessableEntity(message=message)
|
||||
|
||||
try:
|
||||
max_value = int(rule.get('max'))
|
||||
max_value = int(rule.max_size)
|
||||
except ValueError:
|
||||
raise exception.TroveError(_(
|
||||
"Invalid or unsupported max value defined in the "
|
||||
@@ -248,13 +243,13 @@ class ConfigurationsController(wsgi.Controller):
|
||||
raise exception.UnprocessableEntity(message=message)
|
||||
|
||||
@staticmethod
|
||||
def _find_type(valueType):
|
||||
if valueType == "boolean":
|
||||
def _find_type(value_type):
|
||||
if value_type == "boolean":
|
||||
return bool
|
||||
elif valueType == "string":
|
||||
elif value_type == "string":
|
||||
return basestring
|
||||
elif valueType == "integer":
|
||||
return int
|
||||
elif value_type == "integer":
|
||||
return (int, long)
|
||||
else:
|
||||
raise exception.TroveError(_(
|
||||
"Invalid or unsupported type defined in the "
|
||||
@@ -273,35 +268,27 @@ class ParametersController(wsgi.Controller):
|
||||
def index(self, req, tenant_id, datastore, id):
|
||||
ds, ds_version = ds_models.get_datastore_version(
|
||||
type=datastore, version=id)
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=ds_version.manager)
|
||||
rules = models.DatastoreConfigurationParameters.load_parameters(
|
||||
ds_version.id)
|
||||
return wsgi.Result(views.ConfigurationParametersView(rules).data(),
|
||||
200)
|
||||
|
||||
def show(self, req, tenant_id, datastore, id, name):
|
||||
ds, ds_version = ds_models.get_datastore_version(
|
||||
type=datastore, version=id)
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=ds_version.manager)
|
||||
for rule in rules['configuration-parameters']:
|
||||
if rule['name'] == name:
|
||||
return wsgi.Result(
|
||||
views.ConfigurationParametersView(rule).data(), 200)
|
||||
raise exception.ConfigKeyNotFound(key=name)
|
||||
rule = models.DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
ds_version.id, name)
|
||||
return wsgi.Result(views.ConfigurationParameterView(rule).data(), 200)
|
||||
|
||||
def index_by_version(self, req, tenant_id, version):
|
||||
ds_version = ds_models.DatastoreVersion.load_by_uuid(version)
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=ds_version.manager)
|
||||
rules = models.DatastoreConfigurationParameters.load_parameters(
|
||||
ds_version.id)
|
||||
return wsgi.Result(views.ConfigurationParametersView(rules).data(),
|
||||
200)
|
||||
|
||||
def show_by_version(self, req, tenant_id, version, name):
|
||||
ds_version = ds_models.DatastoreVersion.load_by_uuid(version)
|
||||
rules = configurations.get_validation_rules(
|
||||
datastore_manager=ds_version.manager)
|
||||
for rule in rules['configuration-parameters']:
|
||||
if rule['name'] == name:
|
||||
return wsgi.Result(
|
||||
views.ConfigurationParametersView(rule).data(), 200)
|
||||
raise exception.ConfigKeyNotFound(key=name)
|
||||
ds_models.DatastoreVersion.load_by_uuid(version)
|
||||
rule = models.DatastoreConfigurationParameters.load_parameter_by_name(
|
||||
version, name)
|
||||
return wsgi.Result(views.ConfigurationParameterView(rule).data(), 200)
|
||||
|
||||
@@ -112,10 +112,33 @@ class DetailedConfigurationView(object):
|
||||
return {"configuration": configuration_dict}
|
||||
|
||||
|
||||
class ConfigurationParametersView(object):
|
||||
class ConfigurationParameterView(object):
|
||||
|
||||
def __init__(self, configuration_parameters):
|
||||
self.configuration_parameters = configuration_parameters
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def data(self):
|
||||
return self.configuration_parameters
|
||||
ret = {
|
||||
"name": self.config.name,
|
||||
"datastore_version_id": self.config.datastore_version_id,
|
||||
"restart_required": self.config.restart_required,
|
||||
"type": self.config.data_type,
|
||||
}
|
||||
if self.config.max_size:
|
||||
ret["max_size"] = self.config.max_size
|
||||
if self.config.min_size:
|
||||
ret["min_size"] = self.config.min_size
|
||||
return ret
|
||||
|
||||
|
||||
class ConfigurationParametersView(object):
|
||||
|
||||
def __init__(self, configs):
|
||||
self.configs = configs
|
||||
|
||||
def data(self):
|
||||
params = []
|
||||
for p in self.configs:
|
||||
param = ConfigurationParameterView(p)
|
||||
params.append(param.data())
|
||||
return {"configuration-parameters": params}
|
||||
|
||||
@@ -65,6 +65,9 @@ def map(engine, models):
|
||||
Table('conductor_lastseen', meta, autoload=True))
|
||||
orm.mapper(models['clusters'],
|
||||
Table('clusters', meta, autoload=True))
|
||||
orm.mapper(models['datastore_configuration_parameters'],
|
||||
Table('datastore_configuration_parameters', meta,
|
||||
autoload=True))
|
||||
|
||||
|
||||
def mapping_exists(model):
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright 2014 Rackspace
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.schema import Column
|
||||
from sqlalchemy.schema import MetaData
|
||||
from sqlalchemy.schema import UniqueConstraint
|
||||
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import create_tables
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import drop_tables
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import DateTime
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import Boolean
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import String
|
||||
from trove.db.sqlalchemy.migrate_repo.schema import Table
|
||||
|
||||
|
||||
meta = MetaData()
|
||||
|
||||
|
||||
datastore_configuration_parameters = Table(
|
||||
'datastore_configuration_parameters',
|
||||
meta,
|
||||
Column('id', String(36), primary_key=True, nullable=False),
|
||||
Column('name', String(128), primary_key=True, nullable=False),
|
||||
Column('datastore_version_id', String(36),
|
||||
ForeignKey("datastore_versions.id"),
|
||||
primary_key=True, nullable=False),
|
||||
Column('restart_required', Boolean(), nullable=False, default=False),
|
||||
Column('max_size', String(40)),
|
||||
Column('min_size', String(40)),
|
||||
Column('data_type', String(128), nullable=False),
|
||||
Column('deleted', Boolean()),
|
||||
Column('deleted_at', DateTime()),
|
||||
UniqueConstraint(
|
||||
'datastore_version_id', 'name',
|
||||
name='UQ_datastore_configuration_parameters_datastore_version_id_name')
|
||||
)
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
Table('datastore_versions', meta, autoload=True)
|
||||
create_tables([datastore_configuration_parameters])
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta.bind = migrate_engine
|
||||
drop_tables([datastore_configuration_parameters])
|
||||
0
trove/extensions/mgmt/configuration/__init__.py
Normal file
0
trove/extensions/mgmt/configuration/__init__.py
Normal file
136
trove/extensions/mgmt/configuration/service.py
Normal file
136
trove/extensions/mgmt/configuration/service.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# Copyright 2014 Rackspace
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from trove.common import exception
|
||||
from trove.common import wsgi
|
||||
from trove.common.auth import admin_context
|
||||
from trove.configuration import models as config_models
|
||||
from trove.datastore import models as ds_models
|
||||
from trove.extensions.mgmt.configuration import views
|
||||
from trove.openstack.common import log as logging
|
||||
from trove.openstack.common.gettextutils import _
|
||||
import trove.common.apischema as apischema
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigurationsParameterController(wsgi.Controller):
|
||||
"""Controller for configuration parameters functionality."""
|
||||
schemas = apischema.mgmt_configuration
|
||||
|
||||
@admin_context
|
||||
def index(self, req, tenant_id, version_id):
|
||||
"""List all configuration parameters."""
|
||||
ds_version = ds_models.DatastoreVersion.load_by_uuid(version_id)
|
||||
config_params = config_models.DatastoreConfigurationParameters
|
||||
rules = config_params.load_parameters(
|
||||
ds_version.id, show_deleted=True)
|
||||
return wsgi.Result(views.MgmtConfigurationParametersView(rules).data(),
|
||||
200)
|
||||
|
||||
@admin_context
|
||||
def show(self, req, tenant_id, version_id, id):
|
||||
"""Show a configuration parameter."""
|
||||
ds_models.DatastoreVersion.load_by_uuid(version_id)
|
||||
config_params = config_models.DatastoreConfigurationParameters
|
||||
rule = config_params.load_parameter_by_name(
|
||||
version_id, id, show_deleted=True)
|
||||
return wsgi.Result(views.MgmtConfigurationParameterView(rule).data(),
|
||||
200)
|
||||
|
||||
def _validate_data_type(self, parameter):
|
||||
min_size = None
|
||||
max_size = None
|
||||
data_type = parameter['data_type']
|
||||
if data_type == "integer":
|
||||
if 'max_size' not in parameter:
|
||||
raise exception.BadRequest(_("max_size is required for "
|
||||
"integer data type."))
|
||||
if 'min_size' not in parameter:
|
||||
raise exception.BadRequest(_("min_size is required for "
|
||||
"integer data type."))
|
||||
max_size = int(parameter['max_size'])
|
||||
min_size = int(parameter['min_size'])
|
||||
if max_size < min_size:
|
||||
raise exception.BadRequest(
|
||||
_("max_size must be greater than or equal to min_size."))
|
||||
return data_type, min_size, max_size
|
||||
|
||||
@admin_context
|
||||
def create(self, req, body, tenant_id, version_id):
|
||||
"""Create configuration parameter for datastore version."""
|
||||
LOG.info(_("Creating configuration parameter for datastore"))
|
||||
LOG.debug("req : '%s'\n\n" % req)
|
||||
LOG.debug("body : '%s'\n\n" % body)
|
||||
if not body:
|
||||
raise exception.BadRequest(_("Invalid request body."))
|
||||
|
||||
parameter = body['configuration-parameter']
|
||||
name = parameter['name']
|
||||
restart_required = bool(parameter['restart_required'])
|
||||
data_type, min_size, max_size = self._validate_data_type(parameter)
|
||||
datastore_version = ds_models.DatastoreVersion.load_by_uuid(version_id)
|
||||
|
||||
rule = config_models.DatastoreConfigurationParameters.create(
|
||||
name=name,
|
||||
datastore_version_id=datastore_version.id,
|
||||
restart_required=restart_required,
|
||||
data_type=data_type,
|
||||
max_size=max_size,
|
||||
min_size=min_size
|
||||
)
|
||||
return wsgi.Result(
|
||||
views.MgmtConfigurationParameterView(rule).data(),
|
||||
200)
|
||||
|
||||
@admin_context
|
||||
def update(self, req, body, tenant_id, version_id, id):
|
||||
"""Updating configuration parameter for datastore version."""
|
||||
LOG.info(_("Updating configuration parameter for datastore"))
|
||||
LOG.debug("req : '%s'\n\n" % req)
|
||||
LOG.debug("body : '%s'\n\n" % body)
|
||||
if not body:
|
||||
raise exception.BadRequest(_("Invalid request body."))
|
||||
|
||||
parameter = body['configuration-parameter']
|
||||
restart_required = bool(parameter['restart_required'])
|
||||
data_type, min_size, max_size = self._validate_data_type(parameter)
|
||||
ds_models.DatastoreVersion.load_by_uuid(version_id)
|
||||
ds_config_params = config_models.DatastoreConfigurationParameters
|
||||
param = ds_config_params.load_parameter_by_name(
|
||||
version_id, id)
|
||||
param.restart_required = restart_required
|
||||
param.data_type = data_type
|
||||
param.max_size = max_size
|
||||
param.min_size = min_size
|
||||
param.save()
|
||||
return wsgi.Result(
|
||||
views.MgmtConfigurationParameterView(param).data(),
|
||||
200)
|
||||
|
||||
@admin_context
|
||||
def delete(self, req, tenant_id, version_id, id):
|
||||
"""Delete configuration parameter for datastore version."""
|
||||
LOG.info(_("Deleting configuration parameter for datastore"))
|
||||
LOG.debug("req : '%s'\n\n" % req)
|
||||
ds_config_params = config_models.DatastoreConfigurationParameters
|
||||
try:
|
||||
ds_config_params.delete(version_id, id)
|
||||
except exception.NotFound:
|
||||
raise exception.BadRequest(_("Parameter %s does not exist in the "
|
||||
"database.") % id)
|
||||
return wsgi.Result(None, 204)
|
||||
53
trove/extensions/mgmt/configuration/views.py
Normal file
53
trove/extensions/mgmt/configuration/views.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# Copyright 2014 Rackspace
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
from trove.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MgmtConfigurationParameterView(object):
|
||||
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
def data(self):
|
||||
ret = {
|
||||
"name": self.config.name,
|
||||
"datastore_version_id": self.config.datastore_version_id,
|
||||
"restart_required": self.config.restart_required,
|
||||
"type": self.config.data_type,
|
||||
"deleted": self.config.deleted,
|
||||
"deleted_at": self.config.deleted_at,
|
||||
}
|
||||
if self.config.max_size:
|
||||
ret["max_size"] = int(self.config.max_size)
|
||||
if self.config.min_size:
|
||||
ret["min_size"] = int(self.config.min_size)
|
||||
return ret
|
||||
|
||||
|
||||
class MgmtConfigurationParametersView(object):
|
||||
|
||||
def __init__(self, configs):
|
||||
self.configs = configs
|
||||
|
||||
def data(self):
|
||||
params = []
|
||||
LOG.debug(self.configs.__dict__)
|
||||
for p in self.configs:
|
||||
param = MgmtConfigurationParameterView(p)
|
||||
params.append(param.data())
|
||||
return {"configuration-parameters": params}
|
||||
@@ -17,6 +17,7 @@ from trove.openstack.common import log as logging
|
||||
|
||||
from trove.common import extensions
|
||||
from trove.extensions.mgmt.clusters.service import ClusterController
|
||||
from trove.extensions.mgmt.configuration import service as conf_service
|
||||
from trove.extensions.mgmt.instances.service import MgmtInstanceController
|
||||
from trove.extensions.mgmt.host.service import HostController
|
||||
from trove.extensions.mgmt.quota.service import QuotaController
|
||||
@@ -94,4 +95,10 @@ class Mgmt(extensions.ExtensionDescriptor):
|
||||
member_actions={})
|
||||
resources.append(upgrade)
|
||||
|
||||
datastore_configuration_parameters = extensions.ResourceExtension(
|
||||
'{tenant_id}/mgmt/datastores/versions/{version_id}/parameters',
|
||||
conf_service.ConfigurationsParameterController(),
|
||||
member_actions={})
|
||||
resources.append(datastore_configuration_parameters)
|
||||
|
||||
return resources
|
||||
|
||||
@@ -23,7 +23,6 @@ from oslo.config.cfg import NoSuchOptError
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.common import template
|
||||
from trove.common.configurations import do_configs_require_restart
|
||||
import trove.common.instance as tr_instance
|
||||
from trove.common.remote import create_dns_client
|
||||
from trove.common.remote import create_guest_client
|
||||
@@ -734,9 +733,9 @@ class Instance(BuiltInstance):
|
||||
# generate an overrides dict to pass into the instance creation
|
||||
# method
|
||||
|
||||
overrides = Configuration.get_configuration_overrides(
|
||||
context, configuration_id)
|
||||
datastore_status = InstanceServiceStatus.create(
|
||||
config = Configuration(context, configuration_id)
|
||||
overrides = config.get_configuration_overrides()
|
||||
service_status = InstanceServiceStatus.create(
|
||||
instance_id=db_info.id,
|
||||
status=tr_instance.ServiceStatuses.NEW)
|
||||
|
||||
@@ -761,7 +760,7 @@ class Instance(BuiltInstance):
|
||||
overrides, slave_of_id,
|
||||
cluster_config)
|
||||
|
||||
return SimpleInstance(context, db_info, datastore_status,
|
||||
return SimpleInstance(context, db_info, service_status,
|
||||
root_password)
|
||||
|
||||
return run_with_quotas(context.tenant,
|
||||
@@ -963,19 +962,16 @@ class Instance(BuiltInstance):
|
||||
config_datastore_version=config_ds_v,
|
||||
instance_datastore_version=inst_ds_v)
|
||||
|
||||
overrides = Configuration.get_configuration_overrides(
|
||||
self.context, configuration.id)
|
||||
|
||||
LOG.debug("Config overrides is %s." % overrides)
|
||||
|
||||
self.update_overrides(overrides)
|
||||
config = Configuration(self.context, configuration.id)
|
||||
LOG.debug("Config config is %s." % config)
|
||||
self.update_db(configuration_id=configuration.id)
|
||||
self.update_overrides(config)
|
||||
|
||||
def update_overrides(self, overrides):
|
||||
def update_overrides(self, config):
|
||||
LOG.debug("Updating or removing overrides for instance %s."
|
||||
% self.id)
|
||||
need_restart = do_configs_require_restart(
|
||||
overrides, datastore_manager=self.ds_version.manager)
|
||||
overrides = config.get_configuration_overrides()
|
||||
need_restart = config.does_configuration_need_restart()
|
||||
LOG.debug("Config overrides has non-dynamic settings, "
|
||||
"requires a restart: %s." % need_restart)
|
||||
if need_restart:
|
||||
|
||||
@@ -31,7 +31,6 @@ from trove.common import cfg
|
||||
from trove.common import template
|
||||
from trove.common import utils
|
||||
from trove.common.utils import try_recover
|
||||
from trove.common.configurations import do_configs_require_restart
|
||||
from trove.common.exception import BackupCreationError
|
||||
from trove.common.exception import GuestError
|
||||
from trove.common.exception import GuestTimeout
|
||||
@@ -976,9 +975,10 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
|
||||
"%s.") % self.id)
|
||||
LOG.debug("overrides: %s" % overrides)
|
||||
LOG.debug("self.ds_version: %s" % self.ds_version.__dict__)
|
||||
# todo(cp16net) How do we know what datastore type we have?
|
||||
need_restart = do_configs_require_restart(
|
||||
overrides, datastore_manager=self.ds_version.manager)
|
||||
LOG.debug("self.configuration.id: %s" % self.configuration.id)
|
||||
# check if the configuration requires a restart of the instance
|
||||
config = Configuration(self.context, self.configuration.id)
|
||||
need_restart = config.does_configuration_need_restart()
|
||||
LOG.debug("do we need a restart?: %s" % need_restart)
|
||||
if need_restart:
|
||||
status = inst_models.InstanceTasks.RESTART_REQUIRED
|
||||
|
||||
@@ -196,8 +196,10 @@ class CreateConfigurations(ConfigurationsTestBase):
|
||||
def test_expected_get_configuration_parameter(self):
|
||||
# tests get on a single parameter to verify it has expected attributes
|
||||
param = 'key_buffer_size'
|
||||
expected_config_params = ['name', 'restart_required', 'max',
|
||||
'min', 'type']
|
||||
expected_config_params = ['name', 'restart_required',
|
||||
'max_size', 'min_size', 'type',
|
||||
'deleted', 'deleted_at',
|
||||
'datastore_version_id']
|
||||
instance_info.dbaas.configuration_parameters.get_parameter(
|
||||
instance_info.dbaas_datastore,
|
||||
instance_info.dbaas_datastore_version,
|
||||
@@ -216,8 +218,14 @@ class CreateConfigurations(ConfigurationsTestBase):
|
||||
def test_configurations_create_invalid_values(self):
|
||||
"""Test create configurations with invalid values."""
|
||||
values = '{"this_is_invalid": 123}'
|
||||
assert_unprocessable(instance_info.dbaas.configurations.create,
|
||||
CONFIG_NAME, values, CONFIG_DESC)
|
||||
try:
|
||||
instance_info.dbaas.configurations.create(
|
||||
CONFIG_NAME,
|
||||
values,
|
||||
CONFIG_DESC)
|
||||
except exceptions.NotFound:
|
||||
resp, body = instance_info.dbaas.client.last_response
|
||||
assert_equal(resp.status, 404)
|
||||
|
||||
@test
|
||||
def test_configurations_create_invalid_value_type(self):
|
||||
@@ -587,7 +595,25 @@ class DeleteConfigurations(ConfigurationsTestBase):
|
||||
instance_info.dbaas.configurations.delete,
|
||||
invalid_configuration_id)
|
||||
|
||||
@test
|
||||
@test(depends_on=[test_delete_invalid_configuration_not_found])
|
||||
def test_delete_configuration_parameter_with_mgmt_api(self):
|
||||
# delete a parameter that is used by a test
|
||||
# connect_timeout
|
||||
ds = instance_info.dbaas_datastore
|
||||
ds_v = instance_info.dbaas_datastore_version
|
||||
version = instance_info.dbaas.datastore_versions.get(
|
||||
ds, ds_v)
|
||||
client = instance_info.dbaas_admin.mgmt_configs
|
||||
config_param_name = sql_variables[1]
|
||||
client.delete(version.id, config_param_name)
|
||||
assert_raises(
|
||||
exceptions.NotFound,
|
||||
instance_info.dbaas.configuration_parameters.get_parameter,
|
||||
ds,
|
||||
ds_v,
|
||||
config_param_name)
|
||||
|
||||
@test(depends_on=[test_delete_configuration_parameter_with_mgmt_api])
|
||||
def test_unable_delete_instance_configurations(self):
|
||||
# test deleting a configuration that is assigned to
|
||||
# an instance is not allowed.
|
||||
|
||||
228
trove/tests/api/mgmt/configurations.py
Normal file
228
trove/tests/api/mgmt/configurations.py
Normal file
@@ -0,0 +1,228 @@
|
||||
# Copyright 2014 Rackspace Hosting
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from troveclient.compat import exceptions
|
||||
|
||||
from proboscis import before_class
|
||||
from proboscis import test
|
||||
from proboscis import asserts
|
||||
|
||||
from trove import tests
|
||||
from trove.tests.util import test_config
|
||||
from trove.tests.util import create_dbaas_client
|
||||
from trove.tests.util.users import Requirements
|
||||
|
||||
GROUP = "dbaas.api.mgmt.configurations"
|
||||
|
||||
|
||||
@test(groups=[GROUP, tests.DBAAS_API, tests.PRE_INSTANCES],
|
||||
depends_on_groups=["services.initialize"])
|
||||
class ConfigGroupsSetupBeforeInstanceCreation(object):
|
||||
|
||||
@before_class
|
||||
def setUp(self):
|
||||
self.user = test_config.users.find_user(Requirements(is_admin=True))
|
||||
self.admin_client = create_dbaas_client(self.user)
|
||||
self.datastore_version_id = self.admin_client.datastore_versions.get(
|
||||
"mysql", "5.5").id
|
||||
|
||||
@test
|
||||
def test_valid_config_create_type(self):
|
||||
name = "testconfig-create"
|
||||
restart_required = 1
|
||||
data_type = "string"
|
||||
max_size = None
|
||||
min_size = None
|
||||
client = self.admin_client.mgmt_configs
|
||||
param_list = client.parameters_by_version(
|
||||
self.datastore_version_id)
|
||||
asserts.assert_true(name not in [p.name for p in param_list])
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
param_list = client.parameters_by_version(
|
||||
self.datastore_version_id)
|
||||
asserts.assert_true(name in [p.name for p in param_list])
|
||||
param = client.get_parameter_by_version(
|
||||
self.datastore_version_id, name)
|
||||
asserts.assert_equal(name, param.name)
|
||||
asserts.assert_equal(restart_required, param.restart_required)
|
||||
asserts.assert_equal(data_type, param.type)
|
||||
|
||||
# test the modify
|
||||
restart_required = 0
|
||||
data_type = "integer"
|
||||
max_size = "10"
|
||||
min_size = "1"
|
||||
client.modify(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
param = client.get_parameter_by_version(
|
||||
self.datastore_version_id, name)
|
||||
asserts.assert_equal(name, param.name)
|
||||
asserts.assert_equal(restart_required, param.restart_required)
|
||||
asserts.assert_equal(data_type, param.type)
|
||||
asserts.assert_equal(max_size, param.max_size)
|
||||
asserts.assert_equal(min_size, param.min_size)
|
||||
client.delete(self.datastore_version_id, name)
|
||||
|
||||
# test show deleted params work
|
||||
param_list = client.list_all_parameter_by_version(
|
||||
self.datastore_version_id)
|
||||
asserts.assert_true(name in [p.name for p in param_list])
|
||||
param = client.get_any_parameter_by_version(
|
||||
self.datastore_version_id, name)
|
||||
asserts.assert_equal(name, param.name)
|
||||
asserts.assert_equal(restart_required, param.restart_required)
|
||||
asserts.assert_equal(data_type, param.type)
|
||||
asserts.assert_equal(int(max_size), int(param.max_size))
|
||||
asserts.assert_equal(int(min_size), int(param.min_size))
|
||||
asserts.assert_equal(True, param.deleted)
|
||||
asserts.assert_true(param.deleted_at)
|
||||
|
||||
def test_create_config_type_twice_fails(self):
|
||||
name = "test-delete-config-types"
|
||||
restart_required = 1
|
||||
data_type = "string"
|
||||
max_size = None
|
||||
min_size = None
|
||||
client = self.admin_client.mgmt_configs
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
asserts.assert_raises(exceptions.BadRequest,
|
||||
client.create,
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
client.delete(self.datastore_version_id, name)
|
||||
config_list = client.parameters_by_version(self.datastore_version_id)
|
||||
asserts.assert_true(name not in [conf.name for conf in config_list])
|
||||
# testing that recreate of a deleted parameter works.
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
config_list = client.parameters_by_version(self.datastore_version_id)
|
||||
asserts.assert_false(name not in [conf.name for conf in config_list])
|
||||
|
||||
@test
|
||||
def test_delete_config_type(self):
|
||||
name = "test-delete-config-types"
|
||||
restart_required = 1
|
||||
data_type = "string"
|
||||
max_size = None
|
||||
min_size = None
|
||||
client = self.admin_client.mgmt_configs
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
client.delete(self.datastore_version_id, name)
|
||||
config_list = client.parameters_by_version(self.datastore_version_id)
|
||||
asserts.assert_true(name not in [conf.name for conf in config_list])
|
||||
|
||||
@test
|
||||
def test_delete_config_type_fail(self):
|
||||
asserts.assert_raises(
|
||||
exceptions.BadRequest,
|
||||
self.admin_client.mgmt_configs.delete,
|
||||
self.datastore_version_id,
|
||||
"test-delete-config-types")
|
||||
|
||||
@test
|
||||
def test_invalid_config_create_type(self):
|
||||
name = "testconfig_invalid_type"
|
||||
restart_required = 1
|
||||
data_type = "other"
|
||||
max_size = None
|
||||
min_size = None
|
||||
asserts.assert_raises(
|
||||
exceptions.BadRequest,
|
||||
self.admin_client.mgmt_configs.create,
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
|
||||
@test
|
||||
def test_invalid_config_create_restart_required(self):
|
||||
name = "testconfig_invalid_restart_required"
|
||||
restart_required = 5
|
||||
data_type = "string"
|
||||
max_size = None
|
||||
min_size = None
|
||||
asserts.assert_raises(
|
||||
exceptions.BadRequest,
|
||||
self.admin_client.mgmt_configs.create,
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
|
||||
@test
|
||||
def test_config_parameter_was_deleted_then_recreate_updates_it(self):
|
||||
name = "test-delete-and-recreate-param"
|
||||
restart_required = 1
|
||||
data_type = "string"
|
||||
max_size = None
|
||||
min_size = None
|
||||
client = self.admin_client.mgmt_configs
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
restart_required,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
client.delete(self.datastore_version_id, name)
|
||||
client.create(
|
||||
self.datastore_version_id,
|
||||
name,
|
||||
0,
|
||||
data_type,
|
||||
max_size,
|
||||
min_size)
|
||||
param_list = client.list_all_parameter_by_version(
|
||||
self.datastore_version_id)
|
||||
asserts.assert_true(name in [p.name for p in param_list])
|
||||
param = client.get_any_parameter_by_version(
|
||||
self.datastore_version_id, name)
|
||||
asserts.assert_equal(False, param.deleted)
|
||||
@@ -64,6 +64,7 @@ class FakeFlavors(object):
|
||||
self._add(10, 2, "m1.rd-tiny", 512)
|
||||
self._add(11, 0, "eph.rd-tiny", 512, 1)
|
||||
self._add(12, 20, "eph.rd-smaller", 768, 2)
|
||||
# self._add(13, 20, "m1.heat", 512)
|
||||
|
||||
def _add(self, *args, **kwargs):
|
||||
new_flavor = FakeFlavor(*args, **kwargs)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import jsonschema
|
||||
from testtools import TestCase
|
||||
from trove.configuration.service import ConfigurationsController
|
||||
from trove.extensions.mgmt.configuration import service
|
||||
from trove.common import configurations
|
||||
|
||||
|
||||
@@ -124,3 +125,80 @@ class TestConfigurationController(TestCase):
|
||||
self.assertIsNotNone(schema)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertTrue(validator.is_valid(body))
|
||||
|
||||
|
||||
class TestConfigurationsParameterController(TestCase):
|
||||
def setUp(self):
|
||||
super(TestConfigurationsParameterController, self).setUp()
|
||||
self.controller = service.ConfigurationsParameterController()
|
||||
|
||||
def test_validate_create_configuration_param(self):
|
||||
body = {
|
||||
'configuration-parameter': {
|
||||
'name': 'test',
|
||||
'restart_required': 1,
|
||||
'data_type': 'string',
|
||||
'min_size': '0',
|
||||
'max_size': '255'
|
||||
}
|
||||
}
|
||||
schema = self.controller.get_schema('create', body)
|
||||
self.assertIsNotNone(schema)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertTrue(validator.is_valid(body))
|
||||
|
||||
def test_validate_create_invalid_restart_required(self):
|
||||
body = {
|
||||
'configuration-parameter': {
|
||||
'name': 'test',
|
||||
'restart_required': 5,
|
||||
'data_type': 'string',
|
||||
'min_size': 0,
|
||||
'max_size': 255
|
||||
}
|
||||
}
|
||||
schema = self.controller.get_schema('create', body)
|
||||
self.assertIsNotNone(schema)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertFalse(validator.is_valid(body))
|
||||
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
|
||||
error_messages = [error.message for error in errors]
|
||||
self.assertIn("5 is greater than the maximum of 1", error_messages)
|
||||
self.assertIn("0 is not of type 'string'", error_messages)
|
||||
self.assertIn("255 is not of type 'string'", error_messages)
|
||||
|
||||
def test_validate_create_invalid_restart_required_2(self):
|
||||
body = {
|
||||
'configuration-parameter': {
|
||||
'name': 'test',
|
||||
'restart_required': -1,
|
||||
'data_type': 'string',
|
||||
'min_size': '0',
|
||||
'max_size': '255'
|
||||
}
|
||||
}
|
||||
schema = self.controller.get_schema('create', body)
|
||||
self.assertIsNotNone(schema)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertFalse(validator.is_valid(body))
|
||||
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
|
||||
error_messages = [error.message for error in errors]
|
||||
self.assertIn("-1 is less than the minimum of 0", error_messages)
|
||||
|
||||
def test_validate_create_invalid_restart_required_3(self):
|
||||
body = {
|
||||
'configuration-parameter': {
|
||||
'name': 'test',
|
||||
'restart_required': 'yes',
|
||||
'data_type': 'string',
|
||||
'min_size': '0',
|
||||
'max_size': '255'
|
||||
}
|
||||
}
|
||||
schema = self.controller.get_schema('create', body)
|
||||
self.assertIsNotNone(schema)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertFalse(validator.is_valid(body))
|
||||
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
|
||||
error_messages = [error.message for error in errors]
|
||||
self.assertIn("'yes' is not of type 'integer'", error_messages)
|
||||
|
||||
Reference in New Issue
Block a user