Use the compute SDK in usage commands
Update usage list and usage show to use the compute component of the OpenStack SDK instead of directly using the nova interface. Change-Id: I1c4d2247c9c1a577ed9efad7e8332e7c9b974ad5
This commit is contained in:
		@@ -15,12 +15,10 @@
 | 
			
		||||
 | 
			
		||||
"""Usage action implementations"""
 | 
			
		||||
 | 
			
		||||
import collections
 | 
			
		||||
import datetime
 | 
			
		||||
import functools
 | 
			
		||||
 | 
			
		||||
from cliff import columns as cliff_columns
 | 
			
		||||
from novaclient import api_versions
 | 
			
		||||
from osc_lib.command import command
 | 
			
		||||
from osc_lib import utils
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +56,7 @@ class ProjectColumn(cliff_columns.FormattableColumn):
 | 
			
		||||
class CountColumn(cliff_columns.FormattableColumn):
 | 
			
		||||
 | 
			
		||||
    def human_readable(self):
 | 
			
		||||
        return len(self._value)
 | 
			
		||||
        return len(self._value) if self._value is not None else None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FloatColumn(cliff_columns.FormattableColumn):
 | 
			
		||||
@@ -69,7 +67,7 @@ class FloatColumn(cliff_columns.FormattableColumn):
 | 
			
		||||
 | 
			
		||||
def _formatters(project_cache):
 | 
			
		||||
    return {
 | 
			
		||||
        'tenant_id': functools.partial(
 | 
			
		||||
        'project_id': functools.partial(
 | 
			
		||||
            ProjectColumn, project_cache=project_cache),
 | 
			
		||||
        'server_usages': CountColumn,
 | 
			
		||||
        'total_memory_mb_usage': FloatColumn,
 | 
			
		||||
@@ -102,10 +100,10 @@ def _merge_usage(usage, next_usage):
 | 
			
		||||
 | 
			
		||||
def _merge_usage_list(usages, next_usage_list):
 | 
			
		||||
    for next_usage in next_usage_list:
 | 
			
		||||
        if next_usage.tenant_id in usages:
 | 
			
		||||
            _merge_usage(usages[next_usage.tenant_id], next_usage)
 | 
			
		||||
        if next_usage.project_id in usages:
 | 
			
		||||
            _merge_usage(usages[next_usage.project_id], next_usage)
 | 
			
		||||
        else:
 | 
			
		||||
            usages[next_usage.tenant_id] = next_usage
 | 
			
		||||
            usages[next_usage.project_id] = next_usage
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListUsage(command.Lister):
 | 
			
		||||
@@ -138,9 +136,9 @@ class ListUsage(command.Lister):
 | 
			
		||||
            else:
 | 
			
		||||
                return project
 | 
			
		||||
 | 
			
		||||
        compute_client = self.app.client_manager.compute
 | 
			
		||||
        compute_client = self.app.client_manager.sdk_connection.compute
 | 
			
		||||
        columns = (
 | 
			
		||||
            "tenant_id",
 | 
			
		||||
            "project_id",
 | 
			
		||||
            "server_usages",
 | 
			
		||||
            "total_memory_mb_usage",
 | 
			
		||||
            "total_vcpus_usage",
 | 
			
		||||
@@ -154,36 +152,25 @@ class ListUsage(command.Lister):
 | 
			
		||||
            "Disk GB-Hours"
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        dateformat = "%Y-%m-%d"
 | 
			
		||||
        date_cli_format = "%Y-%m-%d"
 | 
			
		||||
        date_api_format = "%Y-%m-%dT%H:%M:%S"
 | 
			
		||||
        now = datetime.datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
        if parsed_args.start:
 | 
			
		||||
            start = datetime.datetime.strptime(parsed_args.start, dateformat)
 | 
			
		||||
            start = datetime.datetime.strptime(
 | 
			
		||||
                parsed_args.start, date_cli_format)
 | 
			
		||||
        else:
 | 
			
		||||
            start = now - datetime.timedelta(weeks=4)
 | 
			
		||||
 | 
			
		||||
        if parsed_args.end:
 | 
			
		||||
            end = datetime.datetime.strptime(parsed_args.end, dateformat)
 | 
			
		||||
            end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
 | 
			
		||||
        else:
 | 
			
		||||
            end = now + datetime.timedelta(days=1)
 | 
			
		||||
 | 
			
		||||
        if compute_client.api_version < api_versions.APIVersion("2.40"):
 | 
			
		||||
            usage_list = compute_client.usage.list(start, end, detailed=True)
 | 
			
		||||
        else:
 | 
			
		||||
            # If the number of instances used to calculate the usage is greater
 | 
			
		||||
            # than CONF.api.max_limit, the usage will be split across multiple
 | 
			
		||||
            # requests and the responses will need to be merged back together.
 | 
			
		||||
            usages = collections.OrderedDict()
 | 
			
		||||
            usage_list = compute_client.usage.list(start, end, detailed=True)
 | 
			
		||||
            _merge_usage_list(usages, usage_list)
 | 
			
		||||
            marker = _get_usage_list_marker(usage_list)
 | 
			
		||||
            while marker:
 | 
			
		||||
                next_usage_list = compute_client.usage.list(
 | 
			
		||||
                    start, end, detailed=True, marker=marker)
 | 
			
		||||
                marker = _get_usage_list_marker(next_usage_list)
 | 
			
		||||
                if marker:
 | 
			
		||||
                    _merge_usage_list(usages, next_usage_list)
 | 
			
		||||
            usage_list = list(usages.values())
 | 
			
		||||
        usage_list = list(compute_client.usages(
 | 
			
		||||
            start=start.strftime(date_api_format),
 | 
			
		||||
            end=end.strftime(date_api_format),
 | 
			
		||||
            detailed=True))
 | 
			
		||||
 | 
			
		||||
        # Cache the project list
 | 
			
		||||
        project_cache = {}
 | 
			
		||||
@@ -196,8 +183,8 @@ class ListUsage(command.Lister):
 | 
			
		||||
 | 
			
		||||
        if parsed_args.formatter == 'table' and len(usage_list) > 0:
 | 
			
		||||
            self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
 | 
			
		||||
                "start": start.strftime(dateformat),
 | 
			
		||||
                "end": end.strftime(dateformat),
 | 
			
		||||
                "start": start.strftime(date_cli_format),
 | 
			
		||||
                "end": end.strftime(date_cli_format),
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
@@ -239,17 +226,19 @@ class ShowUsage(command.ShowOne):
 | 
			
		||||
 | 
			
		||||
    def take_action(self, parsed_args):
 | 
			
		||||
        identity_client = self.app.client_manager.identity
 | 
			
		||||
        compute_client = self.app.client_manager.compute
 | 
			
		||||
        dateformat = "%Y-%m-%d"
 | 
			
		||||
        compute_client = self.app.client_manager.sdk_connection.compute
 | 
			
		||||
        date_cli_format = "%Y-%m-%d"
 | 
			
		||||
        date_api_format = "%Y-%m-%dT%H:%M:%S"
 | 
			
		||||
        now = datetime.datetime.utcnow()
 | 
			
		||||
 | 
			
		||||
        if parsed_args.start:
 | 
			
		||||
            start = datetime.datetime.strptime(parsed_args.start, dateformat)
 | 
			
		||||
            start = datetime.datetime.strptime(
 | 
			
		||||
                parsed_args.start, date_cli_format)
 | 
			
		||||
        else:
 | 
			
		||||
            start = now - datetime.timedelta(weeks=4)
 | 
			
		||||
 | 
			
		||||
        if parsed_args.end:
 | 
			
		||||
            end = datetime.datetime.strptime(parsed_args.end, dateformat)
 | 
			
		||||
            end = datetime.datetime.strptime(parsed_args.end, date_cli_format)
 | 
			
		||||
        else:
 | 
			
		||||
            end = now + datetime.timedelta(days=1)
 | 
			
		||||
 | 
			
		||||
@@ -262,19 +251,21 @@ class ShowUsage(command.ShowOne):
 | 
			
		||||
            # Get the project from the current auth
 | 
			
		||||
            project = self.app.client_manager.auth_ref.project_id
 | 
			
		||||
 | 
			
		||||
        usage = compute_client.usage.get(project, start, end)
 | 
			
		||||
        usage = compute_client.get_usage(
 | 
			
		||||
            project=project, start=start.strftime(date_api_format),
 | 
			
		||||
            end=end.strftime(date_api_format))
 | 
			
		||||
 | 
			
		||||
        if parsed_args.formatter == 'table':
 | 
			
		||||
            self.app.stdout.write(_(
 | 
			
		||||
                "Usage from %(start)s to %(end)s on project %(project)s: \n"
 | 
			
		||||
            ) % {
 | 
			
		||||
                "start": start.strftime(dateformat),
 | 
			
		||||
                "end": end.strftime(dateformat),
 | 
			
		||||
                "start": start.strftime(date_cli_format),
 | 
			
		||||
                "end": end.strftime(date_cli_format),
 | 
			
		||||
                "project": project,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        columns = (
 | 
			
		||||
            "tenant_id",
 | 
			
		||||
            "project_id",
 | 
			
		||||
            "server_usages",
 | 
			
		||||
            "total_memory_mb_usage",
 | 
			
		||||
            "total_vcpus_usage",
 | 
			
		||||
 
 | 
			
		||||
@@ -11,11 +11,8 @@
 | 
			
		||||
#   under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import datetime
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
from novaclient import api_versions
 | 
			
		||||
 | 
			
		||||
from openstackclient.compute.v2 import usage as usage_cmds
 | 
			
		||||
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
 | 
			
		||||
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
 | 
			
		||||
@@ -26,8 +23,9 @@ class TestUsage(compute_fakes.TestComputev2):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestUsage, self).setUp()
 | 
			
		||||
 | 
			
		||||
        self.usage_mock = self.app.client_manager.compute.usage
 | 
			
		||||
        self.usage_mock.reset_mock()
 | 
			
		||||
        self.app.client_manager.sdk_connection = mock.Mock()
 | 
			
		||||
        self.app.client_manager.sdk_connection.compute = mock.Mock()
 | 
			
		||||
        self.sdk_client = self.app.client_manager.sdk_connection.compute
 | 
			
		||||
 | 
			
		||||
        self.projects_mock = self.app.client_manager.identity.projects
 | 
			
		||||
        self.projects_mock.reset_mock()
 | 
			
		||||
@@ -38,7 +36,7 @@ class TestUsageList(TestUsage):
 | 
			
		||||
    project = identity_fakes.FakeProject.create_one_project()
 | 
			
		||||
    # Return value of self.usage_mock.list().
 | 
			
		||||
    usages = compute_fakes.FakeUsage.create_usages(
 | 
			
		||||
        attrs={'tenant_id': project.name}, count=1)
 | 
			
		||||
        attrs={'project_id': project.name}, count=1)
 | 
			
		||||
 | 
			
		||||
    columns = (
 | 
			
		||||
        "Project",
 | 
			
		||||
@@ -49,7 +47,7 @@ class TestUsageList(TestUsage):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data = [(
 | 
			
		||||
        usage_cmds.ProjectColumn(usages[0].tenant_id),
 | 
			
		||||
        usage_cmds.ProjectColumn(usages[0].project_id),
 | 
			
		||||
        usage_cmds.CountColumn(usages[0].server_usages),
 | 
			
		||||
        usage_cmds.FloatColumn(usages[0].total_memory_mb_usage),
 | 
			
		||||
        usage_cmds.FloatColumn(usages[0].total_vcpus_usage),
 | 
			
		||||
@@ -59,7 +57,7 @@ class TestUsageList(TestUsage):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestUsageList, self).setUp()
 | 
			
		||||
 | 
			
		||||
        self.usage_mock.list.return_value = self.usages
 | 
			
		||||
        self.sdk_client.usages.return_value = self.usages
 | 
			
		||||
 | 
			
		||||
        self.projects_mock.list.return_value = [self.project]
 | 
			
		||||
        # Get the command object to test
 | 
			
		||||
@@ -97,9 +95,9 @@ class TestUsageList(TestUsage):
 | 
			
		||||
        columns, data = self.cmd.take_action(parsed_args)
 | 
			
		||||
 | 
			
		||||
        self.projects_mock.list.assert_called_with()
 | 
			
		||||
        self.usage_mock.list.assert_called_with(
 | 
			
		||||
            datetime.datetime(2016, 11, 11, 0, 0),
 | 
			
		||||
            datetime.datetime(2016, 12, 20, 0, 0),
 | 
			
		||||
        self.sdk_client.usages.assert_called_with(
 | 
			
		||||
            start='2016-11-11T00:00:00',
 | 
			
		||||
            end='2016-12-20T00:00:00',
 | 
			
		||||
            detailed=True)
 | 
			
		||||
 | 
			
		||||
        self.assertCountEqual(self.columns, columns)
 | 
			
		||||
@@ -112,20 +110,13 @@ class TestUsageList(TestUsage):
 | 
			
		||||
            ('end', None),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        self.app.client_manager.compute.api_version = api_versions.APIVersion(
 | 
			
		||||
            '2.40')
 | 
			
		||||
        self.usage_mock.list.reset_mock()
 | 
			
		||||
        self.usage_mock.list.side_effect = [self.usages, []]
 | 
			
		||||
 | 
			
		||||
        parsed_args = self.check_parser(self.cmd, arglist, verifylist)
 | 
			
		||||
 | 
			
		||||
        columns, data = self.cmd.take_action(parsed_args)
 | 
			
		||||
 | 
			
		||||
        self.projects_mock.list.assert_called_with()
 | 
			
		||||
        self.usage_mock.list.assert_has_calls([
 | 
			
		||||
            mock.call(mock.ANY, mock.ANY, detailed=True),
 | 
			
		||||
            mock.call(mock.ANY, mock.ANY, detailed=True,
 | 
			
		||||
                      marker=self.usages[0]['server_usages'][0]['instance_id'])
 | 
			
		||||
        self.sdk_client.usages.assert_has_calls([
 | 
			
		||||
            mock.call(start=mock.ANY, end=mock.ANY, detailed=True)
 | 
			
		||||
        ])
 | 
			
		||||
        self.assertCountEqual(self.columns, columns)
 | 
			
		||||
        self.assertCountEqual(tuple(self.data), tuple(data))
 | 
			
		||||
@@ -136,7 +127,7 @@ class TestUsageShow(TestUsage):
 | 
			
		||||
    project = identity_fakes.FakeProject.create_one_project()
 | 
			
		||||
    # Return value of self.usage_mock.list().
 | 
			
		||||
    usage = compute_fakes.FakeUsage.create_one_usage(
 | 
			
		||||
        attrs={'tenant_id': project.name})
 | 
			
		||||
        attrs={'project_id': project.name})
 | 
			
		||||
 | 
			
		||||
    columns = (
 | 
			
		||||
        'Project',
 | 
			
		||||
@@ -147,7 +138,7 @@ class TestUsageShow(TestUsage):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    data = (
 | 
			
		||||
        usage_cmds.ProjectColumn(usage.tenant_id),
 | 
			
		||||
        usage_cmds.ProjectColumn(usage.project_id),
 | 
			
		||||
        usage_cmds.CountColumn(usage.server_usages),
 | 
			
		||||
        usage_cmds.FloatColumn(usage.total_memory_mb_usage),
 | 
			
		||||
        usage_cmds.FloatColumn(usage.total_vcpus_usage),
 | 
			
		||||
@@ -157,7 +148,7 @@ class TestUsageShow(TestUsage):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestUsageShow, self).setUp()
 | 
			
		||||
 | 
			
		||||
        self.usage_mock.get.return_value = self.usage
 | 
			
		||||
        self.sdk_client.get_usage.return_value = self.usage
 | 
			
		||||
 | 
			
		||||
        self.projects_mock.get.return_value = self.project
 | 
			
		||||
        # Get the command object to test
 | 
			
		||||
@@ -199,10 +190,10 @@ class TestUsageShow(TestUsage):
 | 
			
		||||
 | 
			
		||||
        columns, data = self.cmd.take_action(parsed_args)
 | 
			
		||||
 | 
			
		||||
        self.usage_mock.get.assert_called_with(
 | 
			
		||||
            self.project.id,
 | 
			
		||||
            datetime.datetime(2016, 11, 11, 0, 0),
 | 
			
		||||
            datetime.datetime(2016, 12, 20, 0, 0))
 | 
			
		||||
        self.sdk_client.get_usage.assert_called_with(
 | 
			
		||||
            project=self.project.id,
 | 
			
		||||
            start='2016-11-11T00:00:00',
 | 
			
		||||
            end='2016-12-20T00:00:00')
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(self.columns, columns)
 | 
			
		||||
        self.assertEqual(self.data, data)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user