From 8e4bfa723d940e5d999ec98fd16912ea58ca4d51 Mon Sep 17 00:00:00 2001 From: Andrey Shestakov Date: Tue, 15 Oct 2013 00:21:44 +0300 Subject: [PATCH] Support datastore types implements blueprint db-type-version Change-Id: I5dec99c44393c04d86c1a8e19c0b3ca59329fd87 --- troveclient/compat/client.py | 4 + troveclient/compat/xml.py | 4 +- troveclient/tests/test_datastores.py | 131 +++++++++++++++++++++++++++ troveclient/tests/test_instances.py | 7 +- troveclient/v1/client.py | 4 + troveclient/v1/datastores.py | 93 +++++++++++++++++++ troveclient/v1/instances.py | 10 +- troveclient/v1/shell.py | 49 +++++++++- 8 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 troveclient/tests/test_datastores.py create mode 100644 troveclient/v1/datastores.py diff --git a/troveclient/compat/client.py b/troveclient/compat/client.py index 030e3833..f0f0ac45 100644 --- a/troveclient/compat/client.py +++ b/troveclient/compat/client.py @@ -308,6 +308,8 @@ class Dbaas(object): from troveclient.v1.backups import Backups from troveclient.v1.security_groups import SecurityGroups from troveclient.v1.security_groups import SecurityGroupRules + from troveclient.v1.datastores import Datastores + from troveclient.v1.datastores import DatastoreVersions from troveclient.v1.storage import StorageInfo from troveclient.v1.management import Management from troveclient.v1.management import MgmtFlavors @@ -334,6 +336,8 @@ class Dbaas(object): self.backups = Backups(self) self.security_groups = SecurityGroups(self) self.security_group_rules = SecurityGroupRules(self) + self.datastores = Datastores(self) + self.datastore_versions = DatastoreVersions(self) self.storage = StorageInfo(self) self.management = Management(self) self.mgmt_flavor = MgmtFlavors(self) diff --git a/troveclient/compat/xml.py b/troveclient/compat/xml.py index b4589934..e01c23c2 100644 --- a/troveclient/compat/xml.py +++ b/troveclient/compat/xml.py @@ -20,7 +20,9 @@ LISTIFY = { "attachments": [[]], "limits": [[]], "security_groups": [[]], - "backups": [[]] + "backups": [[]], + "datastores": [[]], + "datastore_versions": [[]] } diff --git a/troveclient/tests/test_datastores.py b/troveclient/tests/test_datastores.py new file mode 100644 index 00000000..b7ad1e09 --- /dev/null +++ b/troveclient/tests/test_datastores.py @@ -0,0 +1,131 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation +# Copyright 2013 Mirantis, 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. + + +from testtools import TestCase +from mock import Mock + +from troveclient.v1 import datastores +from troveclient import base + + +""" +Unit tests for datastores.py +""" + + +class DatastoreTest(TestCase): + + def setUp(self): + super(DatastoreTest, self).setUp() + self.orig__init = datastores.Datastore.__init__ + datastores.Datastore.__init__ = Mock(return_value=None) + self.datastore = datastores.Datastore() + self.datastore.manager = Mock() + + def tearDown(self): + super(DatastoreTest, self).tearDown() + datastores.Datastore.__init__ = self.orig__init + + def test___repr__(self): + self.datastore.name = "datastore-1" + self.assertEqual('', + self.datastore.__repr__()) + + +class DatastoresTest(TestCase): + + def setUp(self): + super(DatastoresTest, self).setUp() + self.orig__init = datastores.Datastores.__init__ + datastores.Datastores.__init__ = Mock(return_value=None) + self.datastores = datastores.Datastores() + self.datastores.api = Mock() + self.datastores.api.client = Mock() + self.datastores.resource_class = Mock(return_value="ds-1") + + self.orig_base_getid = base.getid + base.getid = Mock(return_value="datastore1") + + def tearDown(self): + super(DatastoresTest, self).tearDown() + datastores.Datastores.__init__ = self.orig__init + base.getid = self.orig_base_getid + + def test_list(self): + def side_effect_func(path, inst, limit, marker): + return path, inst, limit, marker + + self.datastores._list = Mock(side_effect=side_effect_func) + limit = "test-limit" + marker = "test-marker" + expected = ("/datastores", "datastores", limit, marker) + self.assertEqual(expected, self.datastores.list(limit, marker)) + + def test_get(self): + def side_effect_func(path, inst): + return path, inst + + self.datastores._get = Mock(side_effect=side_effect_func) + self.assertEqual(('/datastores/datastore1', + 'datastore'), + self.datastores.get(1)) + + +class DatastoreVersionsTest(TestCase): + + def setUp(self): + super(DatastoreVersionsTest, self).setUp() + self.orig__init = datastores.DatastoreVersions.__init__ + datastores.DatastoreVersions.__init__ = Mock(return_value=None) + self.datastore_versions = datastores.DatastoreVersions() + self.datastore_versions.api = Mock() + self.datastore_versions.api.client = Mock() + self.datastore_versions.resource_class = Mock( + return_value="ds_version-1") + + self.orig_base_getid = base.getid + base.getid = Mock(return_value="datastore_version1") + + def tearDown(self): + super(DatastoreVersionsTest, self).tearDown() + datastores.DatastoreVersions.__init__ = self.orig__init + base.getid = self.orig_base_getid + + def test_list(self): + def side_effect_func(path, inst, limit, marker): + return path, inst, limit, marker + + self.datastore_versions._list = Mock(side_effect=side_effect_func) + limit = "test-limit" + marker = "test-marker" + expected = ("/datastores/datastore1/versions", + "versions", limit, marker) + self.assertEqual(expected, self.datastore_versions.list( + "datastore1", limit, marker)) + + def test_get(self): + def side_effect_func(path, inst): + return path, inst + + self.datastore_versions._get = Mock(side_effect=side_effect_func) + self.assertEqual(('/datastores/datastore1/versions/' + 'datastore_version1', + 'version'), + self.datastore_versions.get("datastore1", + "datastore_version1")) diff --git a/troveclient/tests/test_instances.py b/troveclient/tests/test_instances.py index 2b74052c..47a57e8a 100644 --- a/troveclient/tests/test_instances.py +++ b/troveclient/tests/test_instances.py @@ -90,13 +90,18 @@ class InstancesTest(TestCase): self.instances._create = Mock(side_effect=side_effect_func) p, b, i = self.instances.create("test-name", 103, "test-volume", - ['db1', 'db2'], ['u1', 'u2']) + ['db1', 'db2'], ['u1', 'u2'], + datastore="datastore", + datastore_version="datastore-version") self.assertEqual("/instances", p) self.assertEqual("instance", i) self.assertEqual(['db1', 'db2'], b["instance"]["databases"]) self.assertEqual(['u1', 'u2'], b["instance"]["users"]) self.assertEqual("test-name", b["instance"]["name"]) self.assertEqual("test-volume", b["instance"]["volume"]) + self.assertEqual("datastore", b["instance"]["datastore"]["type"]) + self.assertEqual("datastore-version", + b["instance"]["datastore"]["version"]) self.assertEqual(103, b["instance"]["flavorRef"]) def test__list(self): diff --git a/troveclient/v1/client.py b/troveclient/v1/client.py index 7431054b..ce68ef4c 100644 --- a/troveclient/v1/client.py +++ b/troveclient/v1/client.py @@ -28,6 +28,8 @@ from troveclient.v1.quota import Quotas from troveclient.v1.backups import Backups from troveclient.v1.security_groups import SecurityGroups from troveclient.v1.security_groups import SecurityGroupRules +from troveclient.v1.datastores import Datastores +from troveclient.v1.datastores import DatastoreVersions from troveclient.v1.storage import StorageInfo from troveclient.v1.management import Management from troveclient.v1.management import MgmtFlavors @@ -71,6 +73,8 @@ class Client(object): self.root = Root(self) self.security_group_rules = SecurityGroupRules(self) self.security_groups = SecurityGroups(self) + self.datastores = Datastores(self) + self.datastore_versions = DatastoreVersions(self) #self.hosts = Hosts(self) #self.quota = Quotas(self) diff --git a/troveclient/v1/datastores.py b/troveclient/v1/datastores.py new file mode 100644 index 00000000..8a92d70e --- /dev/null +++ b/troveclient/v1/datastores.py @@ -0,0 +1,93 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation +# Copyright 2013 Mirantis, 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. + +from troveclient import base + +from troveclient.common import check_for_exceptions +from troveclient.common import limit_url +from troveclient.common import Paginated +from troveclient.openstack.common.apiclient import exceptions +from troveclient.openstack.common.py3kcompat import urlutils + + +class Datastore(base.Resource): + + def __repr__(self): + return "" % self.name + + +class DatastoreVersion(base.Resource): + + def __repr__(self): + return "" % self.name + + +class Datastores(base.ManagerWithFind): + """ + Manage :class:`Datastore` resources. + """ + resource_class = Datastore + + def __repr__(self): + return "" % id(self) + + def list(self, limit=None, marker=None): + """ + Get a list of all datastores. + + :rtype: list of :class:`Datastore`. + """ + return self._list("/datastores", "datastores", limit, marker) + + def get(self, datastore): + """ + Get a specific datastore. + + :rtype: :class:`Datastore` + """ + return self._get("/datastores/%s" % base.getid(datastore), + "datastore") + + +class DatastoreVersions(base.ManagerWithFind): + """ + Manage :class:`DatastoreVersion` resources. + """ + resource_class = DatastoreVersion + + def __repr__(self): + return "" % id(self) + + def list(self, datastore, limit=None, marker=None): + """ + Get a list of all datastore versions. + + :rtype: list of :class:`DatastoreVersion`. + """ + return self._list("/datastores/%s/versions" % datastore, + "versions", limit, marker) + + def get(self, datastore, datastore_version): + """ + Get a specific datastore version. + + :rtype: :class:`DatastoreVersion` + """ + return self._get("/datastores/%s/versions/%s" % + (datastore, base.getid(datastore_version)), + "version") diff --git a/troveclient/v1/instances.py b/troveclient/v1/instances.py index 4c0fb3bd..035f9576 100644 --- a/troveclient/v1/instances.py +++ b/troveclient/v1/instances.py @@ -59,7 +59,8 @@ class Instances(base.ManagerWithFind): resource_class = Instance def create(self, name, flavor_id, volume=None, databases=None, users=None, - restorePoint=None, availability_zone=None): + restorePoint=None, availability_zone=None, datastore=None, + datastore_version=None): """ Create (boot) a new instance. """ @@ -67,6 +68,7 @@ class Instances(base.ManagerWithFind): "name": name, "flavorRef": flavor_id }} + datastore_obj = {} if volume: body["instance"]["volume"] = volume if databases: @@ -77,6 +79,12 @@ class Instances(base.ManagerWithFind): body["instance"]["restorePoint"] = restorePoint if availability_zone: body["instance"]["availability_zone"] = availability_zone + if datastore: + datastore_obj["type"] = datastore + if datastore_version: + datastore_obj["version"] = datastore_version + if datastore_obj: + body["instance"]["datastore"] = datastore_obj return self._create("/instances", body, "instance") diff --git a/troveclient/v1/shell.py b/troveclient/v1/shell.py index 48569343..73fad2c6 100644 --- a/troveclient/v1/shell.py +++ b/troveclient/v1/shell.py @@ -154,6 +154,14 @@ def do_delete(cs, args): metavar='', default=None, help='The Zone hint to give to nova') +@utils.arg('--datastore', + metavar='', + default=None, + help='A datastore name or UUID') +@utils.arg('--datastore_version', + metavar='', + default=None, + help='A datastore version name or UUID') @utils.service_type('database') def do_create(cs, args): """Creates a new instance.""" @@ -172,7 +180,9 @@ def do_create(cs, args): databases=databases, users=users, restorePoint=restore_point, - availability_zone=args.availability_zone) + availability_zone=args.availability_zone, + datastore=args.datastore, + datastore_version=args.datastore_version) instance._info['flavor'] = instance.flavor['id'] if hasattr(instance, 'volume'): instance._info['volume'] = instance.volume['size'] @@ -509,3 +519,40 @@ def do_secgroup_add_rule(cs, args): def do_secgroup_delete_rule(cs, args): """Deletes a security group rule.""" cs.security_group_rules.delete(args.security_group_rule) + + +@utils.service_type('database') +def do_datastore_list(cs, args): + """List all the datastores.""" + datastores = cs.datastores.list() + utils.print_list(datastores, ['id', 'name']) + + +@utils.arg('datastore', metavar='', + help='ID of the datastore.') +@utils.service_type('database') +def do_datastore_show(cs, args): + """Show details of an datastore.""" + datastore = cs.datastores.get(args.datastore) + _print_instance(datastore) + + +@utils.arg('datastore', metavar='', + help='ID of the datastore.') +@utils.service_type('database') +def do_datastore_version_list(cs, args): + """List all the datastore versions.""" + datastore_versions = cs.datastore_versions.list(args.datastore) + utils.print_list(datastore_versions, ['id', 'name']) + + +@utils.arg('datastore', metavar='', + help='ID of the datastore.') +@utils.arg('datastore_version', metavar='', + help='ID of the datastore version.') +@utils.service_type('database') +def do_datastore_version_show(cs, args): + """Show details of an datastore version.""" + datastore_version = cs.datastore_versions.get(args.datastore, + args.datastore_version) + _print_instance(datastore_version)