Uses glance to get image_id when creating a cluster

This PR uses glance to get a valid image_id when creating a cluster.
Clusters API currently checks the "image_id" column in "datastore_versions"
table, which has no valid image_id(NULL) since Victoria due to the
following code and docs change[1].

The original problem of this patch is that nova api throws BadRequest
exception with "Block Device Mapping is Invalid" message when creating
a cluster.

[1]: 1d24b65052

Task: 45004
Story: 2009982
Change-Id: I2a2932e261f47d9c8ec275bf9bd41b26983230c7
This commit is contained in:
Hirotaka Wakabayashi 2022-04-22 13:41:05 +09:00
parent 8182e3d93a
commit 2f52b2fddb
11 changed files with 51 additions and 19 deletions

View File

@ -260,14 +260,16 @@ class Cluster(object):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
locality = srv_grp.ServerGroup.build_scheduler_hint( locality = srv_grp.ServerGroup.build_scheduler_hint(
context, locality, name) context, locality, name)
api_strategy = strategy.load_api_strategy(datastore_version.manager) api_strategy = strategy.load_api_strategy(datastore_version.manager)
return api_strategy.cluster_class.create(context, name, datastore, return api_strategy.cluster_class.create(context, name, datastore,
datastore_version, instances, datastore_version, instances,
extended_properties, extended_properties,
locality, configuration) locality, configuration,
image_id)
def validate_cluster_available(self, valid_states=[ClusterTasks.NONE]): def validate_cluster_available(self, valid_states=[ClusterTasks.NONE]):
if self.db_info.task_status not in valid_states: if self.db_info.task_status not in valid_states:
@ -326,7 +328,18 @@ class Cluster(object):
instance_type = instance_type.split(',') instance_type = instance_type.split(',')
instance['instance_type'] = instance_type instance['instance_type'] = instance_type
instances.append(instance) instances.append(instance)
return self.grow(instances)
# Since Victoria, guest agent uses docker.
# Get image_id from glance if image_id in datastore_versions
# table is NULL.
image_id = self.ds_version.image_id
if not image_id:
glance_client = clients.create_glance_client(context)
image_id = common_glance.get_image_id(
glance_client, self.ds_version.image_id,
self.ds_version.image_tags)
return self.grow(instances, image_id)
elif action == 'shrink': elif action == 'shrink':
context.notification = DBaaSClusterShrink(context, request=req) context.notification = DBaaSClusterShrink(context, request=req)
instance_ids = [instance['id'] for instance in param] instance_ids = [instance['id'] for instance in param]
@ -371,7 +384,7 @@ class Cluster(object):
else: else:
raise exception.BadRequest(_("Action %s not supported") % action) raise exception.BadRequest(_("Action %s not supported") % action)
def grow(self, instances): def grow(self, instances, image_id=None):
raise exception.BadRequest(_("Action 'grow' not supported")) raise exception.BadRequest(_("Action 'grow' not supported"))
def shrink(self, instance_ids): def shrink(self, instance_ids):

View File

@ -20,7 +20,9 @@ from trove.cluster import models
from trove.cluster import views from trove.cluster import views
from trove.common import apischema from trove.common import apischema
from trove.common import cfg from trove.common import cfg
from trove.common import clients
from trove.common import exception from trove.common import exception
from trove.common import glance as common_glance
from trove.common.i18n import _ from trove.common.i18n import _
from trove.common import notification from trove.common import notification
from trove.common.notification import StartNotification from trove.common.notification import StartNotification
@ -172,6 +174,16 @@ class ClusterController(wsgi.Controller):
datastore, datastore_version = ( datastore, datastore_version = (
datastore_models.get_datastore_version(**datastore_args)) datastore_models.get_datastore_version(**datastore_args))
# Since Victoria, guest agent uses docker.
# Get image_id from glance if image_id in datastore_versions table
# is NULL.
image_id = None
if not datastore_version.image_id:
glance_client = clients.create_glance_client(context)
image_id = common_glance.get_image_id(
glance_client, datastore_version.image_id,
datastore_version.image_tags)
extended_properties = body['cluster'].get('extended_properties', {}) extended_properties = body['cluster'].get('extended_properties', {})
try: try:
@ -228,7 +240,8 @@ class ClusterController(wsgi.Controller):
cluster = models.Cluster.create(context, name, datastore, cluster = models.Cluster.create(context, name, datastore,
datastore_version, instances, datastore_version, instances,
extended_properties, extended_properties,
locality, configuration) locality, configuration,
image_id)
cluster.locality = locality cluster.locality = locality
view = views.load_view(cluster, req=req, load_servers=False) view = views.load_view(cluster, req=req, load_servers=False)
return wsgi.Result(view.data(), 200) return wsgi.Result(view.data(), 200)

View File

@ -81,7 +81,8 @@ class CassandraCluster(models.Cluster):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
LOG.debug("Processing a request for creating a new cluster.") LOG.debug("Processing a request for creating a new cluster.")
# Updating Cluster Task. # Updating Cluster Task.

View File

@ -78,7 +78,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
@staticmethod @staticmethod
def _create_instances(context, db_info, datastore, datastore_version, def _create_instances(context, db_info, datastore, datastore_version,
instances, extended_properties, locality, instances, extended_properties, locality,
configuration_id): configuration_id, image_id):
member_config = {"id": db_info.id, member_config = {"id": db_info.id,
"instance_type": "member"} "instance_type": "member"}
name_index = int(time.time()) name_index = int(time.time())
@ -91,7 +91,8 @@ class GaleraCommonCluster(cluster_models.Cluster):
return [Instance.create(context, return [Instance.create(context,
instance['name'], instance['name'],
instance['flavor_id'], instance['flavor_id'],
datastore_version.image_id, datastore_version.image_id
if datastore_version.image_id else image_id,
[], [], [], [],
datastore, datastore_version, datastore, datastore_version,
instance.get('volume_size', None), instance.get('volume_size', None),
@ -111,7 +112,8 @@ class GaleraCommonCluster(cluster_models.Cluster):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
LOG.debug("Initiating Galera cluster creation.") LOG.debug("Initiating Galera cluster creation.")
ds_conf = CONF.get(datastore_version.manager) ds_conf = CONF.get(datastore_version.manager)
# Check number of instances is at least min_cluster_member_count # Check number of instances is at least min_cluster_member_count
@ -129,7 +131,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
cls._create_instances(context, db_info, datastore, datastore_version, cls._create_instances(context, db_info, datastore, datastore_version,
instances, extended_properties, locality, instances, extended_properties, locality,
configuration) configuration, image_id)
# Calling taskmanager to further proceed for cluster-configuration # Calling taskmanager to further proceed for cluster-configuration
task_api.load(context, datastore_version.manager).create_cluster( task_api.load(context, datastore_version.manager).create_cluster(
@ -137,7 +139,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
return cls(context, db_info, datastore, datastore_version) return cls(context, db_info, datastore, datastore_version)
def grow(self, instances): def grow(self, instances, image_id=None):
LOG.debug("Growing cluster %s.", self.id) LOG.debug("Growing cluster %s.", self.id)
self.validate_cluster_available() self.validate_cluster_available()
@ -156,7 +158,7 @@ class GaleraCommonCluster(cluster_models.Cluster):
configuration_id = self.db_info.configuration_id configuration_id = self.db_info.configuration_id
new_instances = self._create_instances( new_instances = self._create_instances(
context, db_info, datastore, datastore_version, instances, context, db_info, datastore, datastore_version, instances,
None, locality, configuration_id) None, locality, configuration_id, image_id)
task_api.load(context, datastore_version.manager).grow_cluster( task_api.load(context, datastore_version.manager).grow_cluster(
db_info.id, [instance.id for instance in new_instances]) db_info.id, [instance.id for instance in new_instances])

View File

@ -58,7 +58,8 @@ class MongoDbCluster(models.Cluster):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
if configuration: if configuration:
raise exception.ConfigurationNotSupported() raise exception.ConfigurationNotSupported()

View File

@ -103,7 +103,8 @@ class RedisCluster(models.Cluster):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
LOG.debug("Initiating cluster creation.") LOG.debug("Initiating cluster creation.")
if configuration: if configuration:

View File

@ -134,7 +134,8 @@ class VerticaCluster(models.Cluster):
@classmethod @classmethod
def create(cls, context, name, datastore, datastore_version, def create(cls, context, name, datastore, datastore_version,
instances, extended_properties, locality, configuration): instances, extended_properties, locality, configuration,
image_id=None):
LOG.debug("Initiating cluster creation.") LOG.debug("Initiating cluster creation.")
if configuration: if configuration:

View File

@ -244,7 +244,7 @@ class TestClusterController(trove_testtools.TestCase):
mock_cluster_create.assert_called_with(context, 'products', mock_cluster_create.assert_called_with(context, 'products',
datastore, datastore_version, datastore, datastore_version,
instances, {}, instances, {},
self.locality, None) self.locality, None, None)
@patch.object(Cluster, 'load') @patch.object(Cluster, 'load')
def test_show_cluster(self, def test_show_cluster(self,

View File

@ -159,7 +159,7 @@ class TestClusterController(trove_testtools.TestCase):
self.controller.create(req, body, tenant_id) self.controller.create(req, body, tenant_id)
mock_cluster_create.assert_called_with(context, 'products', mock_cluster_create.assert_called_with(context, 'products',
datastore, datastore_version, datastore, datastore_version,
instances, {}, None, None) instances, {}, None, None, None)
@patch.object(Cluster, 'load') @patch.object(Cluster, 'load')
def test_show_cluster(self, def test_show_cluster(self,

View File

@ -196,7 +196,7 @@ class TestClusterController(trove_testtools.TestCase):
self.controller.create(req, body, tenant_id) self.controller.create(req, body, tenant_id)
mock_cluster_create.assert_called_with(context, 'products', mock_cluster_create.assert_called_with(context, 'products',
datastore, datastore_version, datastore, datastore_version,
instances, {}, None, None) instances, {}, None, None, None)
@patch.object(Cluster, 'load') @patch.object(Cluster, 'load')
def test_show_cluster(self, def test_show_cluster(self,

View File

@ -159,7 +159,7 @@ class TestClusterController(trove_testtools.TestCase):
self.controller.create(req, body, tenant_id) self.controller.create(req, body, tenant_id)
mock_cluster_create.assert_called_with(context, 'products', mock_cluster_create.assert_called_with(context, 'products',
datastore, datastore_version, datastore, datastore_version,
instances, {}, None, None) instances, {}, None, None, None)
@patch.object(Cluster, 'load') @patch.object(Cluster, 'load')
def test_show_cluster(self, def test_show_cluster(self,