218 lines
8.3 KiB
Python
218 lines
8.3 KiB
Python
# Copyright [2015] Hewlett-Packard Development Company, L.P.
|
|
# Copyright 2016 Tesora Inc.
|
|
# 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 oslo_log import log as logging
|
|
|
|
import time
|
|
|
|
from trove.cluster import models as cluster_models
|
|
from trove.cluster.tasks import ClusterTasks
|
|
from trove.cluster.views import ClusterView
|
|
from trove.common import cfg
|
|
from trove.common import exception
|
|
from trove.common import server_group as srv_grp
|
|
from trove.common.strategies.cluster import base as cluster_base
|
|
from trove.extensions.mgmt.clusters.views import MgmtClusterView
|
|
from trove.instance.models import DBInstance
|
|
from trove.instance.models import Instance
|
|
from trove.quota.quota import check_quotas
|
|
from trove.taskmanager import api as task_api
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
|
|
|
|
class GaleraCommonAPIStrategy(cluster_base.BaseAPIStrategy):
|
|
|
|
@property
|
|
def cluster_class(self):
|
|
return GaleraCommonCluster
|
|
|
|
@property
|
|
def cluster_view_class(self):
|
|
return GaleraCommonClusterView
|
|
|
|
@property
|
|
def mgmt_cluster_view_class(self):
|
|
return GaleraCommonMgmtClusterView
|
|
|
|
|
|
class GaleraCommonCluster(cluster_models.Cluster):
|
|
|
|
@staticmethod
|
|
def _validate_cluster_instances(context, instances, datastore,
|
|
datastore_version):
|
|
"""Validate the flavor and volume"""
|
|
ds_conf = CONF.get(datastore_version.manager)
|
|
num_instances = len(instances)
|
|
|
|
# Checking volumes and get delta for quota check
|
|
cluster_models.validate_instance_flavors(
|
|
context, instances, ds_conf.volume_support, ds_conf.device_path)
|
|
|
|
req_volume_size = cluster_models.get_required_volume_size(
|
|
instances, ds_conf.volume_support)
|
|
|
|
cluster_models.assert_homogeneous_cluster(instances)
|
|
|
|
deltas = {'instances': num_instances, 'volumes': req_volume_size}
|
|
|
|
# quota check
|
|
check_quotas(context.project_id, deltas)
|
|
|
|
# Checking networks are same for the cluster
|
|
cluster_models.validate_instance_nics(context, instances)
|
|
|
|
@staticmethod
|
|
def _create_instances(context, db_info, datastore, datastore_version,
|
|
instances, extended_properties, locality,
|
|
configuration_id):
|
|
member_config = {"id": db_info.id,
|
|
"instance_type": "member"}
|
|
name_index = int(time.time())
|
|
for instance in instances:
|
|
if not instance.get("name"):
|
|
instance['name'] = "%s-member-%s" % (db_info.name,
|
|
str(name_index))
|
|
name_index += 1
|
|
|
|
return [Instance.create(context,
|
|
instance['name'],
|
|
instance['flavor_id'],
|
|
datastore_version.image_id,
|
|
[], [],
|
|
datastore, datastore_version,
|
|
instance.get('volume_size', None),
|
|
None,
|
|
availability_zone=instance.get(
|
|
'availability_zone', None),
|
|
nics=instance.get('nics', None),
|
|
configuration_id=configuration_id,
|
|
cluster_config=member_config,
|
|
volume_type=instance.get(
|
|
'volume_type', None),
|
|
modules=instance.get('modules'),
|
|
locality=locality,
|
|
region_name=instance.get('region_name')
|
|
)
|
|
for instance in instances]
|
|
|
|
@classmethod
|
|
def create(cls, context, name, datastore, datastore_version,
|
|
instances, extended_properties, locality, configuration):
|
|
LOG.debug("Initiating Galera cluster creation.")
|
|
ds_conf = CONF.get(datastore_version.manager)
|
|
# Check number of instances is at least min_cluster_member_count
|
|
if len(instances) < ds_conf.min_cluster_member_count:
|
|
raise exception.ClusterNumInstancesNotLargeEnough(
|
|
num_instances=ds_conf.min_cluster_member_count)
|
|
cls._validate_cluster_instances(context, instances, datastore,
|
|
datastore_version)
|
|
# Updating Cluster Task
|
|
db_info = cluster_models.DBCluster.create(
|
|
name=name, tenant_id=context.project_id,
|
|
datastore_version_id=datastore_version.id,
|
|
task_status=ClusterTasks.BUILDING_INITIAL,
|
|
configuration_id=configuration)
|
|
|
|
cls._create_instances(context, db_info, datastore, datastore_version,
|
|
instances, extended_properties, locality,
|
|
configuration)
|
|
|
|
# Calling taskmanager to further proceed for cluster-configuration
|
|
task_api.load(context, datastore_version.manager).create_cluster(
|
|
db_info.id)
|
|
|
|
return cls(context, db_info, datastore, datastore_version)
|
|
|
|
def grow(self, instances):
|
|
LOG.debug("Growing cluster %s.", self.id)
|
|
|
|
self.validate_cluster_available()
|
|
|
|
context = self.context
|
|
db_info = self.db_info
|
|
datastore = self.ds
|
|
datastore_version = self.ds_version
|
|
|
|
self._validate_cluster_instances(context, instances, datastore,
|
|
datastore_version)
|
|
|
|
db_info.update(task_status=ClusterTasks.GROWING_CLUSTER)
|
|
try:
|
|
locality = srv_grp.ServerGroup.convert_to_hint(self.server_group)
|
|
configuration_id = self.db_info.configuration_id
|
|
new_instances = self._create_instances(
|
|
context, db_info, datastore, datastore_version, instances,
|
|
None, locality, configuration_id)
|
|
|
|
task_api.load(context, datastore_version.manager).grow_cluster(
|
|
db_info.id, [instance.id for instance in new_instances])
|
|
except Exception:
|
|
db_info.update(task_status=ClusterTasks.NONE)
|
|
raise
|
|
|
|
return self.__class__(context, db_info,
|
|
datastore, datastore_version)
|
|
|
|
def shrink(self, instances):
|
|
"""Removes instances from a cluster."""
|
|
LOG.debug("Shrinking cluster %s.", self.id)
|
|
|
|
self.validate_cluster_available()
|
|
removal_instances = [Instance.load(self.context, inst_id)
|
|
for inst_id in instances]
|
|
db_instances = DBInstance.find_all(
|
|
cluster_id=self.db_info.id, deleted=False).all()
|
|
if len(db_instances) - len(removal_instances) < 1:
|
|
raise exception.ClusterShrinkMustNotLeaveClusterEmpty()
|
|
|
|
self.db_info.update(task_status=ClusterTasks.SHRINKING_CLUSTER)
|
|
try:
|
|
task_api.load(self.context, self.ds_version.manager
|
|
).shrink_cluster(self.db_info.id,
|
|
[instance.id
|
|
for instance in removal_instances])
|
|
except Exception:
|
|
self.db_info.update(task_status=ClusterTasks.NONE)
|
|
raise
|
|
|
|
return self.__class__(self.context, self.db_info,
|
|
self.ds, self.ds_version)
|
|
|
|
def restart(self):
|
|
self.rolling_restart()
|
|
|
|
def upgrade(self, datastore_version):
|
|
self.rolling_upgrade(datastore_version)
|
|
|
|
def configuration_attach(self, configuration_id):
|
|
self.rolling_configuration_update(configuration_id)
|
|
|
|
def configuration_detach(self):
|
|
self.rolling_configuration_remove()
|
|
|
|
|
|
class GaleraCommonClusterView(ClusterView):
|
|
|
|
def build_instances(self):
|
|
return self._build_instances(['member'], ['member'])
|
|
|
|
|
|
class GaleraCommonMgmtClusterView(MgmtClusterView):
|
|
|
|
def build_instances(self):
|
|
return self._build_instances(['member'], ['member'])
|