trove/trove/common/server_group.py
Peter Stachowski 187725fafb Locality support for replication
In order to allow replication sets to be all on the same hypervisor
(affinity) or all on different hypervisors (anti-affinity) a new
argument (locality) needed to be added to the Trove create API.

This changeset addresses the Trove server part of this feature.
'locality' can now be added to the ReST payload for a create command
and it is passed along as a scheduler hint to Nova.

The replication scenario tests were enhanced to test that 'affinity'
works and 'anti-affinity' fails (since devstack sets up a single
hypervisor by default). A check for the existance (and
lack of) server-groups was added. This is to ensure that not only is
the server-group created properly, but also that it has been deleted
after all the related instances are gone.

DocImpact: New functionality

Partially implements: blueprint replication-cluster-locality
Depends-On: I18f242983775526a7f1e2644302ebdc0dac025cf
Change-Id: I7d924c25d832f9ff4386e9497bfd214f1b2b3503
2016-06-20 19:38:48 +00:00

97 lines
3.3 KiB
Python

# Copyright 2016 Tesora, Inc.
# 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.
#
import six
from oslo_log import log as logging
from trove.common import cfg
from trove.common.i18n import _
from trove.common.remote import create_nova_client
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
class ServerGroup(object):
@classmethod
def load(cls, context, compute_id):
client = create_nova_client(context)
server_group = None
try:
for sg in client.server_groups.list():
if compute_id in sg.members:
server_group = sg
except Exception:
LOG.exception(_("Could not load server group for compute %s") %
compute_id)
return server_group
@classmethod
def create(cls, context, locality, name_suffix):
client = create_nova_client(context)
server_group_name = "%s_%s" % ('locality', name_suffix)
server_group = client.server_groups.create(
name=server_group_name, policies=[locality])
LOG.debug("Created '%s' server group called %s (id: %s)." %
(locality, server_group_name, server_group.id))
return server_group
@classmethod
def delete(cls, context, server_group, force=False):
# Only delete the server group if we're the last member in it, or if
# it has no members
if server_group:
if force or len(server_group.members) <= 1:
client = create_nova_client(context)
client.server_groups.delete(server_group.id)
LOG.debug("Deleted server group %s." % server_group.id)
else:
LOG.debug("Skipping delete of server group %s (members: %s)." %
(server_group.id, server_group.members))
@classmethod
def convert_to_hint(cls, server_group, hints=None):
if server_group:
hints = hints or {}
hints["group"] = server_group.id
return hints
@classmethod
def build_scheduler_hint(cls, context, locality, name_suffix):
scheduler_hint = None
if locality:
# Build the scheduler hint, but only if locality's a string
if isinstance(locality, six.string_types):
server_group = cls.create(
context, locality, name_suffix)
scheduler_hint = cls.convert_to_hint(
server_group)
else:
# otherwise assume it's already in hint form (i.e. a dict)
scheduler_hint = locality
return scheduler_hint
@classmethod
def get_locality(cls, server_group):
locality = None
if server_group:
locality = server_group.policies[0]
return locality