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 client part of this feature. A --locality flag is now available on the 'create' command and is passed to the server for processing. The --replica_count argument was also cleaned up in that it's not passed on unless it is set. If --replica_of is specified, the default is set to '1.' DocImpact: New functionality Partially implements: blueprint replication-cluster-locality Change-Id: I18f242983775526a7f1e2644302ebdc0dac025cf
This commit is contained in:
parent
260bb64c70
commit
358dbf3c80
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- A --locality flag was added to the trove create command
|
||||
to allow a user to specify whether new replicas should
|
||||
be on the same hypervisor (affinity) or on different
|
||||
hypervisors (anti-affinity).
|
@ -595,6 +595,9 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
def get_instances_1234_root(self, **kw):
|
||||
return (200, {}, {"rootEnabled": 'True'})
|
||||
|
||||
def get_instances_master_1(self, **kw):
|
||||
return (200, {}, {"instance": {"id": 'myid'}})
|
||||
|
||||
def get_clusters_cls_1234_root(self, **kw):
|
||||
return (200, {}, {"rootEnabled": 'True'})
|
||||
|
||||
|
@ -100,7 +100,9 @@ class InstancesTest(testtools.TestCase):
|
||||
datastore="datastore",
|
||||
datastore_version="datastore-version",
|
||||
nics=nics, slave_of='test',
|
||||
modules=['mod_id'])
|
||||
replica_count=4,
|
||||
modules=['mod_id'],
|
||||
locality='affinity')
|
||||
self.assertEqual("/instances", p)
|
||||
self.assertEqual("instance", i)
|
||||
self.assertEqual(['db1', 'db2'], b["instance"]["databases"])
|
||||
@ -118,6 +120,8 @@ class InstancesTest(testtools.TestCase):
|
||||
self.assertNotIn('slave_of', b['instance'])
|
||||
self.assertTrue(mock_warn.called)
|
||||
self.assertEqual([{'id': 'mod_id'}], b["instance"]["modules"])
|
||||
self.assertEqual(4, b["instance"]["replica_count"])
|
||||
self.assertEqual('affinity', b["instance"]["locality"])
|
||||
|
||||
def test_list(self):
|
||||
page_mock = mock.Mock()
|
||||
|
@ -261,8 +261,7 @@ class ShellTest(utils.TestCase):
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': 'lvm'},
|
||||
'flavorRef': 1,
|
||||
'name': 'test-member-1',
|
||||
'replica_count': 1
|
||||
'name': 'test-member-1'
|
||||
}})
|
||||
|
||||
def test_boot_with_modules(self):
|
||||
@ -274,7 +273,6 @@ class ShellTest(utils.TestCase):
|
||||
'volume': {'size': 1, 'type': 'lvm'},
|
||||
'flavorRef': 1,
|
||||
'name': 'test-member-1',
|
||||
'replica_count': 1,
|
||||
'modules': [{'id': '4321'}, {'id': '8765'}]
|
||||
}})
|
||||
|
||||
@ -286,10 +284,67 @@ class ShellTest(utils.TestCase):
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': 'lvm'},
|
||||
'flavorRef': 1,
|
||||
'name': 'test-member-1',
|
||||
'name': 'test-member-1'
|
||||
}})
|
||||
|
||||
def test_boot_repl_set(self):
|
||||
self.run_command('create repl-1 1 --size 1 --locality=anti-affinity '
|
||||
'--replica_count=4')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/instances',
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': None},
|
||||
'flavorRef': 1,
|
||||
'name': 'repl-1',
|
||||
'replica_count': 4,
|
||||
'locality': 'anti-affinity'
|
||||
}})
|
||||
|
||||
def test_boot_replica(self):
|
||||
self.run_command('create slave-1 1 --size 1 --replica_of=master_1')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/instances',
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': None},
|
||||
'flavorRef': 1,
|
||||
'name': 'slave-1',
|
||||
'replica_of': 'myid',
|
||||
'replica_count': 1
|
||||
}})
|
||||
|
||||
def test_boot_replica_count(self):
|
||||
self.run_command('create slave-1 1 --size 1 --replica_of=master_1 '
|
||||
'--replica_count=3')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/instances',
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': None},
|
||||
'flavorRef': 1,
|
||||
'name': 'slave-1',
|
||||
'replica_of': 'myid',
|
||||
'replica_count': 3
|
||||
}})
|
||||
|
||||
def test_boot_locality(self):
|
||||
self.run_command('create master-1 1 --size 1 --locality=affinity')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/instances',
|
||||
{'instance': {
|
||||
'volume': {'size': 1, 'type': None},
|
||||
'flavorRef': 1,
|
||||
'name': 'master-1',
|
||||
'locality': 'affinity'
|
||||
}})
|
||||
|
||||
def test_boot_locality_error(self):
|
||||
cmd = ('create slave-1 1 --size 1 --locality=affinity '
|
||||
'--replica_of=master_1')
|
||||
self.assertRaisesRegexp(
|
||||
exceptions.ValidationError,
|
||||
'Cannot specify locality when adding replicas to existing '
|
||||
'master.',
|
||||
self.run_command, cmd)
|
||||
|
||||
def test_boot_nic_error(self):
|
||||
cmd = ('create test-member-1 1 --size 1 --volume_type lvm '
|
||||
'--nic net-id=some-id,port-id=some-id')
|
||||
@ -307,7 +362,6 @@ class ShellTest(utils.TestCase):
|
||||
'flavorRef': 1,
|
||||
'name': 'test-restore-1',
|
||||
'restorePoint': {'backupRef': 'bk-1234'},
|
||||
'replica_count': 1
|
||||
}})
|
||||
|
||||
def test_boot_restore_by_name(self):
|
||||
@ -319,7 +373,6 @@ class ShellTest(utils.TestCase):
|
||||
'flavorRef': 1,
|
||||
'name': 'test-restore-1',
|
||||
'restorePoint': {'backupRef': 'bk-1234'},
|
||||
'replica_count': 1
|
||||
}})
|
||||
|
||||
def test_cluster_create(self):
|
||||
|
@ -89,7 +89,7 @@ class Instances(base.ManagerWithFind):
|
||||
restorePoint=None, availability_zone=None, datastore=None,
|
||||
datastore_version=None, nics=None, configuration=None,
|
||||
replica_of=None, slave_of=None, replica_count=None,
|
||||
modules=None):
|
||||
modules=None, locality=None):
|
||||
"""Create (boot) a new instance."""
|
||||
|
||||
body = {"instance": {
|
||||
@ -129,6 +129,8 @@ class Instances(base.ManagerWithFind):
|
||||
body["instance"]["replica_count"] = replica_count
|
||||
if modules:
|
||||
body["instance"]["modules"] = self._get_module_list(modules)
|
||||
if locality:
|
||||
body["instance"]["locality"] = locality
|
||||
|
||||
return self._create("/instances", body, "instance")
|
||||
|
||||
|
@ -26,6 +26,7 @@ INSTANCE_ERROR = ("Instance argument(s) must be of the form --instance "
|
||||
NIC_ERROR = ("Invalid NIC argument: %s. Must specify either net-id or port-id "
|
||||
"but not both. Please refer to help.")
|
||||
NO_LOG_FOUND_ERROR = "ERROR: No published '%s' log was found for %s."
|
||||
LOCALITY_DOMAIN = ['affinity', 'anti-affinity']
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
@ -470,26 +471,42 @@ def do_update(cs, args):
|
||||
@utils.arg('--replica_count',
|
||||
metavar='<count>',
|
||||
type=int,
|
||||
default=1,
|
||||
help='Number of replicas to create (defaults to %(default)s).')
|
||||
default=None,
|
||||
help='Number of replicas to create (defaults to 1 if replica_of '
|
||||
'specified).')
|
||||
@utils.arg('--module', metavar='<module>',
|
||||
type=str, dest='modules', action='append', default=[],
|
||||
help='ID or name of the module to apply. Specify multiple '
|
||||
'times to apply multiple modules.')
|
||||
@utils.arg('--locality',
|
||||
metavar='<policy>',
|
||||
default=None,
|
||||
choices=LOCALITY_DOMAIN,
|
||||
help='Locality policy to use when creating replicas. Choose '
|
||||
'one of %(choices)s.')
|
||||
@utils.service_type('database')
|
||||
def do_create(cs, args):
|
||||
"""Creates a new instance."""
|
||||
volume = None
|
||||
replica_of_instance = None
|
||||
flavor_id = _find_flavor(cs, args.flavor).id
|
||||
volume = None
|
||||
if args.size:
|
||||
volume = {"size": args.size,
|
||||
"type": args.volume_type}
|
||||
restore_point = None
|
||||
if args.backup:
|
||||
restore_point = {"backupRef": _find_backup(cs, args.backup).id}
|
||||
replica_of = None
|
||||
replica_count = args.replica_count
|
||||
if args.replica_of:
|
||||
replica_of_instance = _find_instance(cs, args.replica_of)
|
||||
replica_of = _find_instance(cs, args.replica_of)
|
||||
replica_count = replica_count or 1
|
||||
locality = None
|
||||
if args.locality:
|
||||
locality = args.locality
|
||||
if replica_of:
|
||||
raise exceptions.ValidationError(
|
||||
'Cannot specify locality when adding replicas to existing '
|
||||
'master.')
|
||||
databases = [{'name': value} for value in args.databases]
|
||||
users = [{'name': n, 'password': p, 'databases': databases} for (n, p) in
|
||||
[z.split(':')[:2] for z in args.users]]
|
||||
@ -514,9 +531,9 @@ def do_create(cs, args):
|
||||
datastore_version=args.datastore_version,
|
||||
nics=nics,
|
||||
configuration=args.configuration,
|
||||
replica_of=replica_of_instance,
|
||||
replica_count=args.replica_count,
|
||||
modules=modules)
|
||||
replica_of=replica_of,
|
||||
replica_count=replica_count,
|
||||
modules=modules, locality=locality)
|
||||
_print_instance(instance)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user