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
This commit is contained in:
Lingxian Kong 2020-10-21 22:25:12 +13:00
parent 6a12e59601
commit bfa2392d16
4 changed files with 111 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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']))