Merge "Implements Datastore Registration API"
This commit is contained in:
commit
8bec82524d
|
@ -605,3 +605,53 @@ upgrade = {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package_list = {
|
||||
"type": "array",
|
||||
"minItems": 0,
|
||||
"uniqueItems": True,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 255,
|
||||
"pattern": "^.*[0-9a-zA-Z]+.*$"
|
||||
}
|
||||
}
|
||||
|
||||
mgmt_datastore_version = {
|
||||
"create": {
|
||||
"name": "mgmt_datastore_version:create",
|
||||
"type": "object",
|
||||
"required": ["version"],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "object",
|
||||
"required": ["name", "datastore_name", "image", "active"],
|
||||
"additionalProperties": True,
|
||||
"properties": {
|
||||
"name": non_empty_string,
|
||||
"datastore_name": non_empty_string,
|
||||
"datastore_manager": non_empty_string,
|
||||
"packages": package_list,
|
||||
"image": uuid,
|
||||
"active": {"enum": [True, False]},
|
||||
"default": {"enum": [True, False]}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"name": "mgmt_datastore_version:edit",
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"additionalProperties": True,
|
||||
"properties": {
|
||||
"datastore_manager": non_empty_string,
|
||||
"packages": package_list,
|
||||
"image": uuid,
|
||||
"active": {"enum": [True, False]},
|
||||
"default": {"enum": [True, False]},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -531,3 +531,13 @@ class BackupTooLarge(TroveError):
|
|||
message = _("Backup is too large for given flavor or volume. "
|
||||
"Backup size: %(backup_size)s GBs. "
|
||||
"Available size: %(disk_size)s GBs.")
|
||||
|
||||
|
||||
class ImageNotFound(NotFound):
|
||||
|
||||
message = _("Image %(uuid)s cannot be found.")
|
||||
|
||||
|
||||
class DatastoreVersionAlreadyExists(BadRequest):
|
||||
|
||||
message = _("A datastore version with the name '%(name)s' already exists.")
|
||||
|
|
|
@ -408,6 +408,9 @@ class DatastoreVersion(object):
|
|||
except exception.ModelNotFoundError:
|
||||
raise exception.DatastoreVersionNotFound(version=uuid)
|
||||
|
||||
def delete(self):
|
||||
self.db_info.delete()
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.db_info.id
|
||||
|
@ -444,6 +447,11 @@ class DatastoreVersion(object):
|
|||
def manager(self):
|
||||
return self.db_info.manager
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
datastore = Datastore.load(self.datastore_id)
|
||||
return (datastore.default_version_id == self.db_info.id)
|
||||
|
||||
@property
|
||||
def capabilities(self):
|
||||
if self._capabilities is None:
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 novaclient import exceptions as nova_exceptions
|
||||
from oslo_log import log as logging
|
||||
|
||||
from trove.common import apischema as apischema
|
||||
from trove.common.auth import admin_context
|
||||
from trove.common import exception
|
||||
from trove.common.i18n import _
|
||||
from trove.common import remote
|
||||
from trove.common import utils
|
||||
from trove.common import wsgi
|
||||
from trove.datastore import models
|
||||
from trove.extensions.mgmt.datastores import views
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DatastoreVersionController(wsgi.Controller):
|
||||
"""Controller for datastore version registration functionality."""
|
||||
|
||||
schemas = apischema.mgmt_datastore_version
|
||||
|
||||
@admin_context
|
||||
def create(self, req, body, tenant_id):
|
||||
"""Adds a new datastore version."""
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
datastore_name = body['version']['datastore_name']
|
||||
version_name = body['version']['name']
|
||||
manager = body['version']['datastore_manager']
|
||||
image_id = body['version']['image']
|
||||
packages = body['version']['packages']
|
||||
if type(packages) is list:
|
||||
packages = ','.join(packages)
|
||||
active = body['version']['active']
|
||||
default = body['version']['default']
|
||||
|
||||
LOG.info(_("Tenant: '%(tenant)s' is adding the datastore "
|
||||
"version: '%(version)s' to datastore: '%(datastore)s'") %
|
||||
{'tenant': tenant_id, 'version': version_name,
|
||||
'datastore': datastore_name})
|
||||
|
||||
client = remote.create_nova_client(context)
|
||||
try:
|
||||
client.images.get(image_id)
|
||||
except nova_exceptions.NotFound:
|
||||
raise exception.ImageNotFound(uuid=image_id)
|
||||
|
||||
try:
|
||||
datastore = models.Datastore.load(datastore_name)
|
||||
except exception.DatastoreNotFound:
|
||||
# Create the datastore if datastore_name does not exists.
|
||||
LOG.info(_("Creating datastore %s") % datastore_name)
|
||||
datastore = models.DBDatastore()
|
||||
datastore.id = utils.generate_uuid()
|
||||
datastore.name = datastore_name
|
||||
datastore.save()
|
||||
|
||||
try:
|
||||
models.DatastoreVersion.load(datastore, version_name)
|
||||
raise exception.DatastoreVersionAlreadyExists(name=version_name)
|
||||
except exception.DatastoreVersionNotFound:
|
||||
models.update_datastore_version(datastore.name, version_name,
|
||||
manager, image_id, packages,
|
||||
active)
|
||||
|
||||
if default:
|
||||
models.update_datastore(datastore.name, version_name)
|
||||
|
||||
return wsgi.Result(None, 202)
|
||||
|
||||
@admin_context
|
||||
def index(self, req, tenant_id):
|
||||
"""Lists all datastore-versions for given datastore."""
|
||||
db_ds_versions = models.DatastoreVersions.load_all(only_active=False)
|
||||
datastore_versions = [models.DatastoreVersion.load_by_uuid(
|
||||
ds_version.id) for ds_version in db_ds_versions]
|
||||
|
||||
return wsgi.Result(
|
||||
views.DatastoreVersionsView(datastore_versions).data(), 200)
|
||||
|
||||
@admin_context
|
||||
def show(self, req, tenant_id, id):
|
||||
"""Lists details of a datastore-version for given datastore."""
|
||||
datastore_version = models.DatastoreVersion.load_by_uuid(id)
|
||||
return wsgi.Result(
|
||||
views.DatastoreVersionView(datastore_version).data(),
|
||||
200)
|
||||
|
||||
@admin_context
|
||||
def edit(self, req, body, tenant_id, id):
|
||||
"""Updates the attributes of a datastore version."""
|
||||
context = req.environ[wsgi.CONTEXT_KEY]
|
||||
datastore_version = models.DatastoreVersion.load_by_uuid(id)
|
||||
|
||||
LOG.info(_("Tenant: '%(tenant)s' is updating the datastore "
|
||||
"version: '%(version)s' for datastore: '%(datastore)s'") %
|
||||
{'tenant': tenant_id, 'version': datastore_version.name,
|
||||
'datastore': datastore_version.datastore_name})
|
||||
|
||||
manager = body.get('datastore_manager', datastore_version.manager)
|
||||
image_id = body.get('image', datastore_version.image_id)
|
||||
active = body.get('active', datastore_version.active)
|
||||
default = body.get('default', None)
|
||||
packages = body.get('packages', datastore_version.packages)
|
||||
if type(packages) is list:
|
||||
packages = ','.join(packages)
|
||||
|
||||
client = remote.create_nova_client(context)
|
||||
try:
|
||||
client.images.get(image_id)
|
||||
except nova_exceptions.NotFound:
|
||||
raise exception.ImageNotFound(uuid=image_id)
|
||||
|
||||
models.update_datastore_version(datastore_version.datastore_name,
|
||||
datastore_version.name,
|
||||
manager, image_id, packages,
|
||||
active)
|
||||
|
||||
if default:
|
||||
models.update_datastore(datastore_version.datastore_name,
|
||||
datastore_version.name)
|
||||
elif (default is False and datastore_version.default is True):
|
||||
models.update_datastore(datastore_version.datastore_name, None)
|
||||
|
||||
return wsgi.Result(None, 202)
|
||||
|
||||
@admin_context
|
||||
def delete(self, req, tenant_id, id):
|
||||
"""Remove an existing datastore version."""
|
||||
datastore_version = models.DatastoreVersion.load_by_uuid(id)
|
||||
datastore = models.Datastore.load(datastore_version.datastore_id)
|
||||
|
||||
LOG.info(_("Tenant: '%(tenant)s' is removing the datastore "
|
||||
"version: '%(version)s' for datastore: '%(datastore)s'") %
|
||||
{'tenant': tenant_id, 'version': datastore_version.name,
|
||||
'datastore': datastore.name})
|
||||
|
||||
if datastore.default_version_id == datastore_version.id:
|
||||
models.update_datastore(datastore.name, None)
|
||||
datastore_version.delete()
|
||||
return wsgi.Result(None, 202)
|
|
@ -0,0 +1,48 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
class DatastoreVersionView(object):
|
||||
|
||||
def __init__(self, datastore_version):
|
||||
self.datastore_version = datastore_version
|
||||
|
||||
def data(self):
|
||||
datastore_version_dict = {
|
||||
"id": self.datastore_version.id,
|
||||
"name": self.datastore_version.name,
|
||||
"datastore_id": self.datastore_version.datastore_id,
|
||||
"datastore_name": self.datastore_version.datastore_name,
|
||||
"datastore_manager": self.datastore_version.manager,
|
||||
"image": self.datastore_version.image_id,
|
||||
"packages": (self.datastore_version.packages.split(
|
||||
',') if self.datastore_version.packages else ['']),
|
||||
"active": self.datastore_version.active,
|
||||
"default": self.datastore_version.default}
|
||||
|
||||
return {'version': datastore_version_dict}
|
||||
|
||||
|
||||
class DatastoreVersionsView(object):
|
||||
|
||||
def __init__(self, datastore_versions):
|
||||
self.datastore_versions = datastore_versions
|
||||
|
||||
def data(self):
|
||||
data = []
|
||||
for datastore_version in self.datastore_versions:
|
||||
data.append(
|
||||
DatastoreVersionView(datastore_version).data()['version'])
|
||||
|
||||
return {'versions': data}
|
|
@ -18,6 +18,7 @@ from oslo_log import log as logging
|
|||
from trove.common import extensions
|
||||
from trove.extensions.mgmt.clusters.service import MgmtClusterController
|
||||
from trove.extensions.mgmt.configuration import service as conf_service
|
||||
from trove.extensions.mgmt.datastores.service import DatastoreVersionController
|
||||
from trove.extensions.mgmt.host.instance import service as hostservice
|
||||
from trove.extensions.mgmt.host.service import HostController
|
||||
from trove.extensions.mgmt.instances.service import MgmtInstanceController
|
||||
|
@ -102,4 +103,10 @@ class Mgmt(extensions.ExtensionDescriptor):
|
|||
member_actions={})
|
||||
resources.append(datastore_configuration_parameters)
|
||||
|
||||
datastore_version = extensions.ResourceExtension(
|
||||
'{tenant_id}/mgmt/datastore-versions',
|
||||
DatastoreVersionController(),
|
||||
member_actions={})
|
||||
resources.append(datastore_version)
|
||||
|
||||
return resources
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 proboscis.asserts import assert_equal
|
||||
from proboscis.asserts import assert_false
|
||||
from proboscis.asserts import assert_raises
|
||||
from proboscis.asserts import assert_true
|
||||
from proboscis import before_class
|
||||
from proboscis.check import Check
|
||||
from proboscis import test
|
||||
from troveclient.compat import exceptions
|
||||
|
||||
from trove.tests.config import CONFIG
|
||||
from trove.tests.util import create_client
|
||||
from trove.tests.util import create_dbaas_client
|
||||
from trove.tests.util import create_nova_client
|
||||
from trove.tests.util import test_config
|
||||
from trove.tests.util.users import Requirements
|
||||
|
||||
GROUP = "dbaas.api.mgmt.ds_versions"
|
||||
|
||||
|
||||
@test(groups=[GROUP])
|
||||
def mgmt_datastore_version_list_requires_admin_account():
|
||||
"""Verify that an admin context is required to call this function."""
|
||||
client = create_client(is_admin=False)
|
||||
assert_raises(exceptions.Unauthorized, client.mgmt_datastore_versions.list)
|
||||
|
||||
|
||||
@test(groups=[GROUP])
|
||||
class MgmtDataStoreVersion(object):
|
||||
"""Tests the mgmt datastore version methods."""
|
||||
|
||||
@before_class
|
||||
def setUp(self):
|
||||
"""Create client for tests."""
|
||||
reqs = Requirements(is_admin=True)
|
||||
self.user = CONFIG.users.find_user(reqs)
|
||||
self.client = create_dbaas_client(self.user)
|
||||
if test_config.nova_client is not None:
|
||||
nova_user = test_config.users.find_user(
|
||||
Requirements(services=["nova"]))
|
||||
self.nova_client = create_nova_client(nova_user)
|
||||
self.images = self.nova_client.images.list()
|
||||
|
||||
def _find_ds_version_by_name(self, ds_version_name):
|
||||
ds_versions = self.client.mgmt_datastore_versions.list()
|
||||
for ds_version in ds_versions:
|
||||
if ds_version_name == ds_version.name:
|
||||
return ds_version
|
||||
|
||||
@test
|
||||
def test_mgmt_ds_version_list_original_count(self):
|
||||
"""Tests the mgmt datastore version list method."""
|
||||
self.ds_versions = self.client.mgmt_datastore_versions.list()
|
||||
# By default we create two datastore-versions for mysql
|
||||
assert_equal(2, len(self.ds_versions.items))
|
||||
|
||||
@test(depends_on=[test_mgmt_ds_version_list_original_count])
|
||||
def test_mgmt_ds_version_list_fields_present(self):
|
||||
"""Verify that all expected fields are returned by list method."""
|
||||
|
||||
expected_fields = [
|
||||
'id',
|
||||
'name',
|
||||
'datastore_id',
|
||||
'datastore_name',
|
||||
'datastore_manager',
|
||||
'image',
|
||||
'packages',
|
||||
'active',
|
||||
'default',
|
||||
]
|
||||
|
||||
for ds_version in self.ds_versions:
|
||||
with Check() as check:
|
||||
for field in expected_fields:
|
||||
check.true(hasattr(ds_version, field),
|
||||
"List lacks field %s." % field)
|
||||
|
||||
@test(depends_on=[test_mgmt_ds_version_list_original_count])
|
||||
def test_mgmt_ds_version_get(self):
|
||||
"""Tests the mgmt datastore version get method."""
|
||||
test_version = self.ds_versions[0]
|
||||
found_ds_version = self.client.mgmt_datastore_versions.get(
|
||||
test_version.id)
|
||||
assert_equal(test_version.name, found_ds_version.name)
|
||||
assert_equal(test_version.datastore_id, found_ds_version.datastore_id)
|
||||
assert_equal(test_version.datastore_name,
|
||||
found_ds_version.datastore_name)
|
||||
assert_equal(test_version.datastore_manager,
|
||||
found_ds_version.datastore_manager)
|
||||
assert_equal(test_version.image, found_ds_version.image)
|
||||
assert_equal(test_version.packages, found_ds_version.packages)
|
||||
assert_equal(test_version.active, found_ds_version.active)
|
||||
assert_equal(test_version.default, found_ds_version.default)
|
||||
|
||||
@test(depends_on=[test_mgmt_ds_version_list_original_count])
|
||||
def test_mgmt_ds_version_create(self):
|
||||
"""Tests the mgmt datastore version create method."""
|
||||
response = self.client.mgmt_datastore_versions.create(
|
||||
'test_version1', 'test_ds', 'test_mgr',
|
||||
self.images[0].id, ['vertica-7.1'])
|
||||
assert_equal(None, response)
|
||||
assert_equal(202, self.client.last_http_code)
|
||||
|
||||
# Since we created one more ds_version
|
||||
# lets check count of total ds_versions, it should be increased by 1
|
||||
new_ds_versions = self.client.mgmt_datastore_versions.list()
|
||||
assert_equal(len(self.ds_versions.items) + 1,
|
||||
len(new_ds_versions.items))
|
||||
|
||||
# Match the contents of newly created ds_version.
|
||||
self.created_version = self._find_ds_version_by_name('test_version1')
|
||||
assert_equal('test_version1', self.created_version.name)
|
||||
assert_equal('test_ds', self.created_version.datastore_name)
|
||||
assert_equal('test_mgr', self.created_version.datastore_manager)
|
||||
assert_equal(self.images[0].id, self.created_version.image)
|
||||
assert_equal(['vertica-7.1'], self.created_version.packages)
|
||||
assert_true(self.created_version.active)
|
||||
assert_false(self.created_version.default)
|
||||
|
||||
@test(depends_on=[test_mgmt_ds_version_create])
|
||||
def test_mgmt_ds_version_patch(self):
|
||||
"""Tests the mgmt datastore version edit method."""
|
||||
self.client.mgmt_datastore_versions.edit(
|
||||
self.created_version.id, image=self.images[1].id,
|
||||
packages=['pkg1'])
|
||||
assert_equal(202, self.client.last_http_code)
|
||||
|
||||
# Lets match the content of patched datastore
|
||||
patched_ds_version = self._find_ds_version_by_name('test_version1')
|
||||
assert_equal(self.images[1].id, patched_ds_version.image)
|
||||
assert_equal(['pkg1'], patched_ds_version.packages)
|
||||
|
||||
@test(depends_on=[test_mgmt_ds_version_patch])
|
||||
def test_mgmt_ds_version_delete(self):
|
||||
"""Tests the mgmt datastore version delete method."""
|
||||
self.client.mgmt_datastore_versions.delete(self.created_version.id)
|
||||
assert_equal(202, self.client.last_http_code)
|
||||
|
||||
# Lets match the total count of ds_version,
|
||||
# it should get back to original
|
||||
ds_versions = self.client.mgmt_datastore_versions.list()
|
||||
assert_equal(len(self.ds_versions.items), len(ds_versions.items))
|
|
@ -23,6 +23,7 @@ from trove.tests.api import instances
|
|||
from trove.tests.api import instances_actions
|
||||
from trove.tests.api.mgmt import accounts
|
||||
from trove.tests.api.mgmt import admin_required
|
||||
from trove.tests.api.mgmt import datastore_versions
|
||||
from trove.tests.api.mgmt import hosts
|
||||
from trove.tests.api.mgmt import instances as mgmt_instances
|
||||
from trove.tests.api.mgmt import storage
|
||||
|
@ -82,6 +83,7 @@ black_box_groups = [
|
|||
instances.GROUP_STOP,
|
||||
versions.GROUP,
|
||||
instances.GROUP_GUEST,
|
||||
datastore_versions.GROUP,
|
||||
]
|
||||
proboscis.register(groups=["blackbox", "mysql"],
|
||||
depends_on_groups=black_box_groups)
|
||||
|
@ -92,6 +94,7 @@ simple_black_box_groups = [
|
|||
versions.GROUP,
|
||||
instances.GROUP_START_SIMPLE,
|
||||
admin_required.GROUP,
|
||||
datastore_versions.GROUP,
|
||||
]
|
||||
proboscis.register(groups=["simple_blackbox"],
|
||||
depends_on_groups=simple_black_box_groups)
|
||||
|
@ -103,6 +106,7 @@ black_box_mgmt_groups = [
|
|||
instances_actions.GROUP_REBOOT,
|
||||
admin_required.GROUP,
|
||||
mgmt_instances.GROUP,
|
||||
datastore_versions.GROUP,
|
||||
]
|
||||
proboscis.register(groups=["blackbox_mgmt"],
|
||||
depends_on_groups=black_box_mgmt_groups)
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import jsonschema
|
||||
|
||||
from mock import Mock, patch, MagicMock, PropertyMock
|
||||
from testtools.matchers import Is, Equals
|
||||
|
||||
from trove.common import exception
|
||||
from trove.common import remote
|
||||
from trove.datastore import models as datastore_models
|
||||
from trove.extensions.mgmt.datastores.service import DatastoreVersionController
|
||||
from trove.tests.unittests import trove_testtools
|
||||
|
||||
|
||||
class TestDatastoreVersionController(trove_testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(TestDatastoreVersionController, self).setUp()
|
||||
self.controller = DatastoreVersionController()
|
||||
|
||||
self.version = {
|
||||
"version": {
|
||||
"datastore_name": "test_dsx",
|
||||
"name": "test_vr1",
|
||||
"datastore_manager": "mysql",
|
||||
"image": "154b350d-4d86-4214-9067-9c54b230c0da",
|
||||
"packages": ["mysql-server-5.6"],
|
||||
"active": True,
|
||||
"default": False
|
||||
}
|
||||
}
|
||||
|
||||
self.tenant_id = Mock()
|
||||
context = Mock()
|
||||
self.req = Mock()
|
||||
self.req.environ = Mock()
|
||||
self.req.environ.__getitem__ = Mock(return_value=context)
|
||||
|
||||
def test_get_schema_create(self):
|
||||
schema = self.controller.get_schema('create', self.version)
|
||||
self.assertIsNotNone(schema)
|
||||
self.assertTrue('version' in schema['properties'])
|
||||
|
||||
def test_validate_create(self):
|
||||
body = self.version
|
||||
schema = self.controller.get_schema('create', body)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertTrue(validator.is_valid(body))
|
||||
|
||||
def test_validate_create_blankname(self):
|
||||
body = self.version
|
||||
body['version']['name'] = " "
|
||||
schema = self.controller.get_schema('create', body)
|
||||
validator = jsonschema.Draft4Validator(schema)
|
||||
self.assertFalse(validator.is_valid(body))
|
||||
errors = sorted(validator.iter_errors(body), key=lambda e: e.path)
|
||||
self.assertThat(len(errors), Is(1))
|
||||
self.assertThat(errors[0].message,
|
||||
Equals("' ' does not match '^.*[0-9a-zA-Z]+.*$'"))
|
||||
|
||||
def test_validate_create_blank_datastore(self):
|
||||
body = self.version
|
||||
body['version']['datastore_name'] = ""
|
||||
schema = self.controller.get_schema('create', body)
|
||||
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.assertThat(len(errors), Is(2))
|
||||
self.assertIn("'' is too short", error_messages)
|
||||
self.assertIn("'' does not match '^.*[0-9a-zA-Z]+.*$'", error_messages)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
@patch.object(datastore_models.Datastore, 'load')
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load',
|
||||
side_effect=exception.DatastoreVersionNotFound)
|
||||
@patch.object(datastore_models, 'update_datastore_version')
|
||||
def test_create_datastore_versions(self, mock_ds_version_create,
|
||||
mock_ds_version_load,
|
||||
mock_ds_load, mock_nova_client):
|
||||
body = self.version
|
||||
mock_ds_load.return_value.name = 'test_dsx'
|
||||
|
||||
self.controller.create(self.req, body, self.tenant_id)
|
||||
mock_ds_version_create.assert_called_with(
|
||||
'test_dsx', 'test_vr1', 'mysql',
|
||||
'154b350d-4d86-4214-9067-9c54b230c0da',
|
||||
'mysql-server-5.6', True)
|
||||
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
def test_show_ds_version(self, mock_ds_version_load):
|
||||
id = Mock()
|
||||
|
||||
self.controller.show(self.req, self.tenant_id, id)
|
||||
mock_ds_version_load.assert_called_with(id)
|
||||
|
||||
@patch.object(datastore_models.Datastore, 'load')
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
def test_delete_ds_version(self, mock_ds_version_load, mock_ds_load):
|
||||
ds_version_id = Mock()
|
||||
ds_version = Mock()
|
||||
mock_ds_version_load.return_value = ds_version
|
||||
self.controller.delete(self.req, self.tenant_id, ds_version_id)
|
||||
ds_version.delete.assert_called_with()
|
||||
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
@patch.object(datastore_models.DatastoreVersions, 'load_all')
|
||||
def test_index_ds_version(self, mock_ds_version_load_all,
|
||||
mock_ds_version_load_by_uuid):
|
||||
mock_id = Mock()
|
||||
mock_ds_version = Mock()
|
||||
mock_ds_version.id = mock_id
|
||||
mock_ds_version_load_all.return_value = [mock_ds_version]
|
||||
|
||||
self.controller.index(self.req, self.tenant_id)
|
||||
mock_ds_version_load_all.assert_called_with(only_active=False)
|
||||
mock_ds_version_load_by_uuid.assert_called_with(mock_id)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
|
||||
@patch.object(datastore_models, 'update_datastore_version')
|
||||
def test_edit_datastore_versions(self, mock_ds_version_update,
|
||||
mock_ds_version_load,
|
||||
mock_nova_client):
|
||||
body = {'image': '21c8805a-a800-4bca-a192-3a5a2519044d'}
|
||||
|
||||
mock_ds_version = MagicMock()
|
||||
type(mock_ds_version).datastore_name = PropertyMock(
|
||||
return_value=self.version['version']['datastore_name'])
|
||||
type(mock_ds_version).name = PropertyMock(
|
||||
return_value=self.version['version']['name'])
|
||||
type(mock_ds_version).image_id = PropertyMock(
|
||||
return_value=self.version['version']['image'])
|
||||
type(mock_ds_version).packages = PropertyMock(
|
||||
return_value=self.version['version']['packages'])
|
||||
type(mock_ds_version).active = PropertyMock(
|
||||
return_value=self.version['version']['active'])
|
||||
type(mock_ds_version).manager = PropertyMock(
|
||||
return_value=self.version['version']['datastore_manager'])
|
||||
mock_ds_version_load.return_value = mock_ds_version
|
||||
|
||||
self.controller.edit(self.req, body, self.tenant_id, Mock())
|
||||
mock_ds_version_update.assert_called_with(
|
||||
'test_dsx', 'test_vr1', 'mysql',
|
||||
'21c8805a-a800-4bca-a192-3a5a2519044d',
|
||||
'mysql-server-5.6', True)
|
|
@ -0,0 +1,168 @@
|
|||
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 mock import Mock, patch
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
|
||||
from trove.common import exception
|
||||
from trove.common import remote
|
||||
from trove.datastore import models
|
||||
from trove.extensions.mgmt.datastores.service import DatastoreVersionController
|
||||
from trove.tests.unittests import trove_testtools
|
||||
from trove.tests.unittests.util import util
|
||||
|
||||
|
||||
class TestDatastoreVersion(trove_testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestDatastoreVersion, self).setUp()
|
||||
util.init_db()
|
||||
models.update_datastore(name='test_ds', default_version=None)
|
||||
models.update_datastore_version(
|
||||
'test_ds', 'test_vr1', 'mysql',
|
||||
'154b350d-4d86-4214-9067-9c54b230c0da', 'pkg-1', '1')
|
||||
models.update_datastore_version(
|
||||
'test_ds', 'test_vr2', 'mysql',
|
||||
'154b350d-4d86-4214-9067-9c54b230c0da', 'pkg-1', '1')
|
||||
self.ds = models.Datastore.load('test_ds')
|
||||
self.ds_version1 = models.DatastoreVersion.load(self.ds, 'test_vr1')
|
||||
self.ds_version2 = models.DatastoreVersion.load(self.ds, 'test_vr2')
|
||||
|
||||
self.context = Mock()
|
||||
self.req = Mock()
|
||||
self.req.environ = Mock()
|
||||
self.req.environ.__getitem__ = Mock(return_value=self.context)
|
||||
self.tenant_id = Mock()
|
||||
self.version_controller = DatastoreVersionController()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestDatastoreVersion, self).tearDown()
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
def test_version_create(self, mock_nova_client):
|
||||
body = {"version": {
|
||||
"datastore_name": "test_ds",
|
||||
"name": "test_vr",
|
||||
"datastore_manager": "mysql",
|
||||
"image": "image-id",
|
||||
"packages": "test-pkg",
|
||||
"active": True,
|
||||
"default": True}}
|
||||
output = self.version_controller.create(
|
||||
self.req, body, self.tenant_id)
|
||||
self.assertEqual(202, output.status)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
@patch.object(models.DatastoreVersion, 'load')
|
||||
def test_fail_already_exists_version_create(self, mock_load,
|
||||
mock_nova_client):
|
||||
body = {"version": {
|
||||
"datastore_name": "test_ds",
|
||||
"name": "test_new_vr",
|
||||
"datastore_manager": "mysql",
|
||||
"image": "image-id",
|
||||
"packages": "test-pkg",
|
||||
"active": True,
|
||||
"default": True}}
|
||||
self.assertRaisesRegexp(
|
||||
exception.DatastoreVersionAlreadyExists,
|
||||
"A datastore version with the name 'test_new_vr' already exists",
|
||||
self.version_controller.create, self.req, body, self.tenant_id)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
def test_fail_image_not_found_version_create(self, mock_nova_client):
|
||||
mock_nova_client.return_value.images.get = Mock(
|
||||
side_effect=nova_exceptions.NotFound(404,
|
||||
"Image id not found image-id"
|
||||
))
|
||||
body = {"version": {
|
||||
"datastore_name": "test_ds",
|
||||
"name": "test_vr",
|
||||
"datastore_manager": "mysql",
|
||||
"image": "image-id",
|
||||
"packages": "test-pkg",
|
||||
"active": True,
|
||||
"default": True}}
|
||||
self.assertRaisesRegexp(
|
||||
exception.ImageNotFound,
|
||||
"Image image-id cannot be found.",
|
||||
self.version_controller.create, self.req, body, self.tenant_id)
|
||||
|
||||
def test_version_delete(self):
|
||||
output = self.version_controller.delete(self.req,
|
||||
self.tenant_id,
|
||||
self.ds_version1.id)
|
||||
err_msg = ("Datastore version '%s' cannot be found." %
|
||||
self.ds_version1.id)
|
||||
|
||||
self.assertEqual(202, output.status)
|
||||
|
||||
# Try to find deleted version, this should raise exception.
|
||||
self.assertRaisesRegexp(
|
||||
exception.DatastoreVersionNotFound,
|
||||
err_msg, models.DatastoreVersion.load_by_uuid, self.ds_version1.id)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
def test_version_update(self, mock_client):
|
||||
body = {"image": "c022f4dc-76ed-4e3f-a25e-33e031f43f8b"}
|
||||
output = self.version_controller.edit(self.req, body,
|
||||
self.tenant_id,
|
||||
self.ds_version2.id)
|
||||
self.assertEqual(202, output.status)
|
||||
|
||||
# Find the details of version updated and match the updated attribute.
|
||||
test_ds_version = models.DatastoreVersion.load_by_uuid(
|
||||
self.ds_version2.id)
|
||||
self.assertEqual(body['image'], test_ds_version.image_id)
|
||||
|
||||
@patch.object(remote, 'create_nova_client')
|
||||
def test_version_update_fail_image_not_found(self, mock_nova_client):
|
||||
mock_nova_client.return_value.images.get = Mock(
|
||||
side_effect=nova_exceptions.NotFound(404,
|
||||
"Image id not found image-id"
|
||||
))
|
||||
body = {"image": "non-existent-image-id"}
|
||||
|
||||
self.assertRaisesRegexp(
|
||||
exception.ImageNotFound,
|
||||
"Image non-existent-image-id cannot be found.",
|
||||
self.version_controller.edit, self.req, body,
|
||||
self.tenant_id, self.ds_version2.id)
|
||||
|
||||
@patch.object(models.Datastore, 'load')
|
||||
def test_version_index(self, mock_load):
|
||||
output = self.version_controller.index(
|
||||
self.req, self.tenant_id)
|
||||
self.assertEqual(200, output.status)
|
||||
|
||||
def test_version_show(self):
|
||||
output = self.version_controller.show(
|
||||
self.req, self.tenant_id, self.ds_version2.id)
|
||||
self.assertEqual(200, output.status)
|
||||
self.assertEqual(self.ds_version2.id,
|
||||
output._data['version']['id'])
|
||||
self.assertEqual(self.ds_version2.name,
|
||||
output._data['version']['name'])
|
||||
self.assertEqual(self.ds_version2.datastore_id,
|
||||
output._data['version']['datastore_id'])
|
||||
self.assertEqual(self.ds_version2.datastore_name,
|
||||
output._data['version']['datastore_name'])
|
||||
self.assertEqual(self.ds_version2.manager,
|
||||
output._data['version']['datastore_manager'])
|
||||
self.assertEqual(self.ds_version2.image_id,
|
||||
output._data['version']['image'])
|
||||
self.assertEqual(self.ds_version2.packages.split(','),
|
||||
output._data['version']['packages'])
|
||||
self.assertEqual(self.ds_version2.active,
|
||||
output._data['version']['active'])
|
Loading…
Reference in New Issue