Redis Clustering Initial Implementation

Implements the CLI and python-API methods to support the
clustering commands, including the new cluster scaling
commands.

Change-Id: Icc9b4eea8ed7db1455692823d29586088cfc9434
Implements: blueprint redis-cluster
This commit is contained in:
Morgan Jones
2015-07-20 11:33:17 -04:00
parent 7ec45dba07
commit d95cefff2a
4 changed files with 97 additions and 2 deletions

View File

@@ -270,6 +270,9 @@ class FakeHTTPClient(base_client.HTTPClient):
assert_has_keys(instance, required=['volume', 'flavorRef'])
return (202, {}, self.get_clusters_cls_1234()[2])
def post_clusters_cls_1234(self, body, **kw):
return (202, {}, None)
def post_instances_1234_action(self, **kw):
return (202, {}, None)

View File

@@ -13,10 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
import mock
import six
import fixtures
import mock
import troveclient.client
from troveclient import exceptions
import troveclient.shell
@@ -26,6 +26,7 @@ import troveclient.v1.shell
class ShellFixture(fixtures.Fixture):
def setUp(self):
super(ShellFixture, self).setUp()
self.shell = troveclient.shell.OpenStackTroveShell()
@@ -232,6 +233,18 @@ class ShellTest(utils.TestCase):
exceptions.CommandError, 'flavor is required',
self.run_command, cmd)
def test_cluster_grow(self):
cmd = ('cluster-grow cls-1234 '
'--instance flavor=2,volume=2 '
'--instance flavor=2,volume=1')
self.run_command(cmd)
self.assert_called('POST', '/clusters/cls-1234')
def test_cluster_shrink(self):
cmd = ('cluster-shrink cls-1234 1234')
self.run_command(cmd)
self.assert_called('POST', '/clusters/cls-1234')
def test_datastore_list(self):
self.run_command('datastore-list')
self.assert_called('GET', '/datastores')

View File

@@ -70,6 +70,15 @@ class Clusters(base.ManagerWithFind):
resp, body = self.api.client.delete(url)
common.check_for_exceptions(resp, body, url)
def _action(self, cluster, body):
"""Perform a cluster "action" -- grow/shrink/etc."""
url = "/clusters/%s" % base.getid(cluster)
resp, body = self.api.client.post(url, body=body)
common.check_for_exceptions(resp, body, url)
if body:
return self.resource_class(self, body['cluster'], loaded=True)
return body
def add_shard(self, cluster):
"""Adds a shard to the specified cluster.
@@ -83,6 +92,24 @@ class Clusters(base.ManagerWithFind):
return self.resource_class(self, body, loaded=True)
return body
def grow(self, cluster, instances=None):
"""Grow a cluster.
:param cluster: The cluster to grow
:param instances: List of instances to add
"""
body = {"grow": instances}
return self._action(cluster, body)
def shrink(self, cluster, instances=None):
"""Shrink a cluster.
:param cluster: The cluster to shrink
:param instances: List of instances to drop
"""
body = {"shrink": instances}
return self._action(cluster, body)
class ClusterStatus(object):

View File

@@ -253,6 +253,58 @@ def do_cluster_instances(cs, args):
obj_is_dict=True)
@utils.arg('--instance',
metavar="<name=name,flavor=flavor_name_or_id,volume=volume>",
action='append',
dest='instances',
default=[],
help="Add an instance to the cluster. Specify "
"multiple times to create multiple instances.")
@utils.arg('cluster', metavar='<cluster>', help='ID or name of the cluster.')
@utils.service_type('database')
def do_cluster_grow(cs, args):
"""Adds more instances to a cluster."""
cluster = _find_cluster(cs, args.cluster)
instances = []
for instance_str in args.instances:
instance_info = {}
for z in instance_str.split(","):
for (k, v) in [z.split("=", 1)[:2]]:
if k == "name":
instance_info[k] = v
elif k == "flavor":
flavor_id = _find_flavor(cs, v).id
instance_info["flavorRef"] = str(flavor_id)
elif k == "volume":
instance_info["volume"] = {"size": v}
else:
instance_info[k] = v
if not instance_info.get('flavorRef'):
raise exceptions.CommandError(
'flavor is required. '
'Instance arguments must be of the form '
'--instance <flavor=flavor_name_or_id,volume=volume,data=data>'
)
instances.append(instance_info)
cs.clusters.grow(cluster, instances=instances)
@utils.arg('cluster', metavar='<cluster>', help='ID or name of the cluster.')
@utils.arg('instances',
nargs='+',
metavar='<instance>',
default=[],
help="Drop instance(s) from the cluster. Specify "
"multiple ids to drop multiple instances.")
@utils.service_type('database')
def do_cluster_shrink(cs, args):
"""Drops instances from a cluster."""
cluster = _find_cluster(cs, args.cluster)
instances = [{'id': _find_instance(cs, instance).id}
for instance in args.instances]
cs.clusters.shrink(cluster, instances=instances)
@utils.arg('instance', metavar='<instance>',
help='ID or name of the instance.')
@utils.service_type('database')