summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLingxian Kong <anlin.kong@gmail.com>2020-10-21 22:25:12 +1300
committerLingxian Kong <anlin.kong@gmail.com>2020-10-21 23:32:10 +0000
commit0ce59e0436230cc19083afca63928d0fdfad7fcc (patch)
treee5102adfe0f8635e58bcb8c29354b683cc0bde31
parentdfb534c1f2f2f9bcec30a2b056195e0ba067a63d (diff)
downloadtrove-0ce59e0436230cc19083afca63928d0fdfad7fcc.tar.gz
Support to get instances of a specified project
This patch also fixes a performance issue when the adm user is getting all the instances. Change-Id: Icd6345d6c97648cdfbfaa8d9edac7315a1409356
-rw-r--r--api-ref/source/instances.inc11
-rw-r--r--trove/extensions/mgmt/instances/models.py17
-rw-r--r--trove/extensions/mgmt/instances/service.py8
-rw-r--r--trove/tests/unittests/extensions/mgmt/instances/test_service.py88
4 files changed, 111 insertions, 13 deletions
diff --git a/api-ref/source/instances.inc b/api-ref/source/instances.inc
index 2987cad6..1279bc21 100644
--- a/api-ref/source/instances.inc
+++ b/api-ref/source/instances.inc
@@ -38,9 +38,14 @@ List database instances(admin)
.. rest_method:: GET /v1.0/{project_id}/mgmt/instances
-Admin only API. Get all the instances, supported filters: deleted,
-include_clustered. Could show more information such as Cinder volume ID, Nova
-server information, etc.
+Admin only API. Get all the instances, Could show more information such as
+Cinder volume ID, Nova server information, etc.
+
+Supported filters:
+
+* ``deleted``.
+* ``include_clustered``.
+* ``project_id``: Get instances of a speficied project.
Normal response codes: 200
diff --git a/trove/extensions/mgmt/instances/models.py b/trove/extensions/mgmt/instances/models.py
index db4d68c5..c70a550e 100644
--- a/trove/extensions/mgmt/instances/models.py
+++ b/trove/extensions/mgmt/instances/models.py
@@ -18,7 +18,6 @@ from oslo_log import log as logging
from trove.common import cfg
from trove.common import clients
from trove.common import exception
-from trove.common.i18n import _
from trove.common import timeutils
from trove.extensions.mysql import models as mysql_models
from trove.instance import models as instance_models
@@ -29,20 +28,24 @@ CONF = cfg.CONF
def load_mgmt_instances(context, deleted=None, client=None,
- include_clustered=None):
+ include_clustered=None, project_id=None):
if not client:
client = clients.create_nova_client(
context, CONF.service_credentials.region_name
)
- mgmt_servers = client.servers.list(search_opts={'all_tenants': 1},
- limit=-1)
+
+ search_opts = {'all_tenants': False}
+ mgmt_servers = client.servers.list(search_opts=search_opts, limit=-1)
LOG.info("Found %d servers in Nova",
len(mgmt_servers if mgmt_servers else []))
+
args = {}
if deleted is not None:
args['deleted'] = deleted
if not include_clustered:
args['cluster_id'] = None
+ if project_id:
+ args['tenant_id'] = project_id
db_infos = instance_models.DBInstance.find_all(**args)
@@ -153,11 +156,11 @@ class MgmtInstances(instance_models.Instances):
def load_instance(context, db, status, server=None):
return SimpleMgmtInstance(context, db, server, status)
- if context is None:
- raise TypeError(_("Argument context not defined."))
find_server = instance_models.create_server_list_matcher(servers)
+
instances = instance_models.Instances._load_servers_status(
load_instance, context, db_infos, find_server)
+
_load_servers(instances, find_server)
return instances
@@ -170,7 +173,7 @@ def _load_servers(instances, find_server):
server = find_server(db.id, db.compute_instance_id)
instance.server = server
except Exception as ex:
- LOG.exception(ex)
+ LOG.warning(ex)
return instances
diff --git a/trove/extensions/mgmt/instances/service.py b/trove/extensions/mgmt/instances/service.py
index 0d14b778..b05b94af 100644
--- a/trove/extensions/mgmt/instances/service.py
+++ b/trove/extensions/mgmt/instances/service.py
@@ -50,8 +50,7 @@ class MgmtInstanceController(InstanceController):
def index(self, req, tenant_id, detailed=False):
"""Return all instances."""
LOG.info("Indexing a database instance for tenant '%(tenant_id)s'\n"
- "req : '%(req)s'\n\n", {
- "tenant_id": tenant_id, "req": req})
+ "req : '%(req)s'\n\n", {"tenant_id": tenant_id, "req": req})
context = req.environ[wsgi.CONTEXT_KEY]
deleted = None
deleted_q = req.GET.get('deleted', '').lower()
@@ -61,9 +60,12 @@ class MgmtInstanceController(InstanceController):
deleted = False
clustered_q = req.GET.get('include_clustered', '').lower()
include_clustered = clustered_q == 'true'
+ project_id = req.GET.get('project_id')
+
try:
instances = models.load_mgmt_instances(
- context, deleted=deleted, include_clustered=include_clustered)
+ context, deleted=deleted, include_clustered=include_clustered,
+ project_id=project_id)
except nova_exceptions.ClientException as e:
LOG.exception(e)
return wsgi.Result(str(e), 403)
diff --git a/trove/tests/unittests/extensions/mgmt/instances/test_service.py b/trove/tests/unittests/extensions/mgmt/instances/test_service.py
new file mode 100644
index 00000000..534b05f3
--- /dev/null
+++ b/trove/tests/unittests/extensions/mgmt/instances/test_service.py
@@ -0,0 +1,88 @@
+# Copyright 2020 Catalyst Cloud
+#
+# 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 unittest import mock
+
+from trove.datastore import models as ds_models
+from trove.extensions.mgmt.instances import service as ins_service
+from trove.instance import models as ins_models
+from trove.instance import service_status as srvstatus
+from trove.tests.unittests import trove_testtools
+from trove.tests.unittests.util import util
+
+
+class TestMgmtInstanceController(trove_testtools.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ util.init_db()
+ cls.controller = ins_service.MgmtInstanceController()
+
+ cls.ds_name = cls.random_name('datastore')
+ ds_models.update_datastore(name=cls.ds_name, default_version=None)
+ ds_models.update_datastore_version(
+ cls.ds_name, 'test_version', 'mysql', cls.random_uuid(), '', '', 1)
+ cls.ds = ds_models.Datastore.load(cls.ds_name)
+ cls.ds_version = ds_models.DatastoreVersion.load(cls.ds,
+ 'test_version')
+
+ cls.ins_name = cls.random_name('instance')
+ cls.project_id = cls.random_uuid()
+ cls.server_id = cls.random_uuid()
+ cls.instance = ins_models.DBInstance.create(
+ name=cls.ins_name, flavor_id=cls.random_uuid(),
+ tenant_id=cls.project_id,
+ volume_size=1,
+ datastore_version_id=cls.ds_version.id,
+ task_status=ins_models.InstanceTasks.BUILDING,
+ compute_instance_id=cls.server_id
+ )
+ ins_models.InstanceServiceStatus.create(
+ instance_id=cls.instance.id,
+ status=srvstatus.ServiceStatuses.NEW
+ )
+
+ super(TestMgmtInstanceController, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ util.cleanup_db()
+
+ super(TestMgmtInstanceController, cls).tearDownClass()
+
+ @mock.patch('trove.common.clients.create_nova_client')
+ def test_index_project_id(self, mock_create_client):
+ req = mock.MagicMock()
+ req.GET = {
+ 'project_id': self.project_id
+ }
+
+ mock_nova_client = mock.MagicMock()
+ mock_nova_client.servers.list.return_value = [
+ mock.MagicMock(id=self.server_id)]
+ mock_create_client.return_value = mock_nova_client
+
+ result = self.controller.index(req, mock.ANY)
+
+ self.assertEqual(200, result.status)
+ data = result.data(None)
+ self.assertEqual(1, len(data['instances']))
+
+ req.GET = {
+ 'project_id': self.random_uuid()
+ }
+
+ result = self.controller.index(req, mock.ANY)
+
+ self.assertEqual(200, result.status)
+ data = result.data(None)
+ self.assertEqual(0, len(data['instances']))