summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-07-11 18:22:06 +0000
committerGerrit Code Review <review@openstack.org>2014-07-11 18:22:06 +0000
commitbc6495c6a19abb67a9af9bc94e82e6c12b1a7b83 (patch)
tree367c6f6c359ebed491cefae63ceabf8ddbe55093 /openstackclient
parent70283744a04f868072edc0a31fe49a3122c4bc6e (diff)
parent4844a257790deef231176557776754dde929f840 (diff)
downloadpython-openstackclient-bc6495c6a19abb67a9af9bc94e82e6c12b1a7b83.tar.gz
Merge "Add basic timing support"
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/clientmanager.py5
-rw-r--r--openstackclient/common/timing.py44
-rw-r--r--openstackclient/compute/client.py4
-rw-r--r--openstackclient/shell.py33
-rw-r--r--openstackclient/tests/common/test_timing.py87
5 files changed, 170 insertions, 3 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index b310f3ac..a2f85aff 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -49,7 +49,7 @@ class ClientManager(object):
user_domain_id=None, user_domain_name=None,
project_domain_id=None, project_domain_name=None,
region_name=None, api_version=None, verify=True,
- trust_id=None):
+ trust_id=None, timing=None):
self._token = token
self._url = url
self._auth_url = auth_url
@@ -67,6 +67,7 @@ class ClientManager(object):
self._api_version = api_version
self._trust_id = trust_id
self._service_catalog = None
+ self.timing = timing
# verify is the Requests-compatible form
self._verify = verify
@@ -116,7 +117,7 @@ def get_extension_modules(group):
setattr(
ClientManager,
- ep.name,
+ module.API_NAME,
ClientCache(
getattr(sys.modules[ep.module_name], 'make_client', None)
),
diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py
new file mode 100644
index 00000000..1c94682c
--- /dev/null
+++ b/openstackclient/common/timing.py
@@ -0,0 +1,44 @@
+# 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.
+#
+
+"""Timing Implementation"""
+
+import logging
+
+from cliff import lister
+
+
+class Timing(lister.Lister):
+ """Show timing data"""
+
+ log = logging.getLogger(__name__ + '.Timing')
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)' % parsed_args)
+
+ column_headers = (
+ 'URL',
+ 'Seconds',
+ )
+
+ results = []
+ total = 0.0
+ for url, start, end in self.app.timing_data:
+ seconds = end - start
+ total += seconds
+ results.append((url, seconds))
+ results.append(('Total', total))
+ return (
+ column_headers,
+ results,
+ )
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index 36391c6d..dc50507e 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -57,7 +57,9 @@ def make_client(instance):
service_type=API_NAME,
# FIXME(dhellmann): what is service_name?
service_name='',
- http_log_debug=http_log_debug)
+ http_log_debug=http_log_debug,
+ timings=instance.timing,
+ )
# Populate the Nova client to skip another auth query to Identity
if instance._url:
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 1d0c5771..28724343 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -32,6 +32,7 @@ from openstackclient.common import clientmanager
from openstackclient.common import commandmanager
from openstackclient.common import exceptions as exc
from openstackclient.common import restapi
+from openstackclient.common import timing
from openstackclient.common import utils
from openstackclient.identity import client as identity_client
@@ -60,6 +61,7 @@ class OpenStackShell(app.App):
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
log = logging.getLogger(__name__)
+ timing_data = []
def __init__(self):
# Patch command.Command to add a default auth_required = True
@@ -303,6 +305,12 @@ class OpenStackShell(app.App):
metavar='<url>',
default=env('OS_URL'),
help='Defaults to env[OS_URL]')
+ parser.add_argument(
+ '--timing',
+ default=False,
+ action='store_true',
+ help="Print API call timing info",
+ )
parser.add_argument(
'--os-identity-api-version',
@@ -410,6 +418,7 @@ class OpenStackShell(app.App):
password=self.options.os_password,
region_name=self.options.os_region_name,
verify=self.verify,
+ timing=self.options.timing,
api_version=self.api_version,
trust_id=self.options.os_trust_id,
)
@@ -499,9 +508,33 @@ class OpenStackShell(app.App):
def clean_up(self, cmd, result, err):
self.log.debug('clean_up %s', cmd.__class__.__name__)
+
if err:
self.log.debug('got an error: %s', err)
+ # Process collected timing data
+ if self.options.timing:
+ # Loop through extensions
+ for mod in self.ext_modules:
+ client = getattr(self.client_manager, mod.API_NAME)
+ if hasattr(client, 'get_timings'):
+ self.timing_data.extend(client.get_timings())
+
+ # Use the Timing pseudo-command to generate the output
+ tcmd = timing.Timing(self, self.options)
+ tparser = tcmd.get_parser('Timing')
+
+ # If anything other than prettytable is specified, force csv
+ format = 'table'
+ # Check the formatter used in the actual command
+ if hasattr(cmd, 'formatter') \
+ and cmd.formatter != cmd._formatter_plugins['table'].obj:
+ format = 'csv'
+
+ sys.stdout.write('\n')
+ targs = tparser.parse_args(['-f', format])
+ tcmd.run(targs)
+
def interact(self):
# NOTE(dtroyer): Maintain the old behaviour for interactive use as
# this path does not call prepare_to_run_command()
diff --git a/openstackclient/tests/common/test_timing.py b/openstackclient/tests/common/test_timing.py
new file mode 100644
index 00000000..aa910b91
--- /dev/null
+++ b/openstackclient/tests/common/test_timing.py
@@ -0,0 +1,87 @@
+# 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.
+#
+
+"""Test Timing pseudo-command"""
+
+from openstackclient.common import timing
+from openstackclient.tests import fakes
+from openstackclient.tests import utils
+
+
+timing_url = 'GET http://localhost:5000'
+timing_start = 1404802774.872809
+timing_end = 1404802775.724802
+
+
+class FakeGenericClient(object):
+
+ def __init__(self, **kwargs):
+ self.auth_token = kwargs['token']
+ self.management_url = kwargs['endpoint']
+
+
+class TestTiming(utils.TestCommand):
+
+ def setUp(self):
+ super(TestTiming, self).setUp()
+
+ self.app.timing_data = []
+
+ self.app.client_manager.compute = FakeGenericClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ self.app.client_manager.volume = FakeGenericClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ # Get the command object to test
+ self.cmd = timing.Timing(self.app, None)
+
+ def test_timing_list_no_data(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = ('URL', 'Seconds')
+ self.assertEqual(collist, columns)
+ datalist = [
+ ('Total', 0.0,)
+ ]
+ self.assertEqual(datalist, data)
+
+ def test_timing_list(self):
+ self.app.timing_data = [
+ (timing_url, timing_start, timing_end),
+ ]
+
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = ('URL', 'Seconds')
+ self.assertEqual(collist, columns)
+ timing_sec = timing_end - timing_start
+ datalist = [
+ (timing_url, timing_sec),
+ ('Total', timing_sec)
+ ]
+ self.assertEqual(datalist, data)