Skip to content

Commit bc6495c

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Add basic timing support"
2 parents 7028374 + 4844a25 commit bc6495c

5 files changed

Lines changed: 170 additions & 3 deletions

File tree

‎openstackclient/common/clientmanager.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __init__(self, token=None, url=None, auth_url=None,
4949
user_domain_id=None, user_domain_name=None,
5050
project_domain_id=None, project_domain_name=None,
5151
region_name=None, api_version=None, verify=True,
52-
trust_id=None):
52+
trust_id=None, timing=None):
5353
self._token = token
5454
self._url = url
5555
self._auth_url = auth_url
@@ -67,6 +67,7 @@ def __init__(self, token=None, url=None, auth_url=None,
6767
self._api_version = api_version
6868
self._trust_id = trust_id
6969
self._service_catalog = None
70+
self.timing = timing
7071

7172
# verify is the Requests-compatible form
7273
self._verify = verify
@@ -116,7 +117,7 @@ def get_extension_modules(group):
116117

117118
setattr(
118119
ClientManager,
119-
ep.name,
120+
module.API_NAME,
120121
ClientCache(
121122
getattr(sys.modules[ep.module_name], 'make_client', None)
122123
),

‎openstackclient/common/timing.py‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Timing Implementation"""
15+
16+
import logging
17+
18+
from cliff import lister
19+
20+
21+
class Timing(lister.Lister):
22+
"""Show timing data"""
23+
24+
log = logging.getLogger(__name__ + '.Timing')
25+
26+
def take_action(self, parsed_args):
27+
self.log.debug('take_action(%s)' % parsed_args)
28+
29+
column_headers = (
30+
'URL',
31+
'Seconds',
32+
)
33+
34+
results = []
35+
total = 0.0
36+
for url, start, end in self.app.timing_data:
37+
seconds = end - start
38+
total += seconds
39+
results.append((url, seconds))
40+
results.append(('Total', total))
41+
return (
42+
column_headers,
43+
results,
44+
)

‎openstackclient/compute/client.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def make_client(instance):
5757
service_type=API_NAME,
5858
# FIXME(dhellmann): what is service_name?
5959
service_name='',
60-
http_log_debug=http_log_debug)
60+
http_log_debug=http_log_debug,
61+
timings=instance.timing,
62+
)
6163

6264
# Populate the Nova client to skip another auth query to Identity
6365
if instance._url:

‎openstackclient/shell.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from openstackclient.common import commandmanager
3333
from openstackclient.common import exceptions as exc
3434
from openstackclient.common import restapi
35+
from openstackclient.common import timing
3536
from openstackclient.common import utils
3637
from openstackclient.identity import client as identity_client
3738

@@ -60,6 +61,7 @@ class OpenStackShell(app.App):
6061
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
6162

6263
log = logging.getLogger(__name__)
64+
timing_data = []
6365

6466
def __init__(self):
6567
# Patch command.Command to add a default auth_required = True
@@ -303,6 +305,12 @@ def build_option_parser(self, description, version):
303305
metavar='<url>',
304306
default=env('OS_URL'),
305307
help='Defaults to env[OS_URL]')
308+
parser.add_argument(
309+
'--timing',
310+
default=False,
311+
action='store_true',
312+
help="Print API call timing info",
313+
)
306314

307315
parser.add_argument(
308316
'--os-identity-api-version',
@@ -410,6 +418,7 @@ def authenticate_user(self):
410418
password=self.options.os_password,
411419
region_name=self.options.os_region_name,
412420
verify=self.verify,
421+
timing=self.options.timing,
413422
api_version=self.api_version,
414423
trust_id=self.options.os_trust_id,
415424
)
@@ -499,9 +508,33 @@ def prepare_to_run_command(self, cmd):
499508

500509
def clean_up(self, cmd, result, err):
501510
self.log.debug('clean_up %s', cmd.__class__.__name__)
511+
502512
if err:
503513
self.log.debug('got an error: %s', err)
504514

515+
# Process collected timing data
516+
if self.options.timing:
517+
# Loop through extensions
518+
for mod in self.ext_modules:
519+
client = getattr(self.client_manager, mod.API_NAME)
520+
if hasattr(client, 'get_timings'):
521+
self.timing_data.extend(client.get_timings())
522+
523+
# Use the Timing pseudo-command to generate the output
524+
tcmd = timing.Timing(self, self.options)
525+
tparser = tcmd.get_parser('Timing')
526+
527+
# If anything other than prettytable is specified, force csv
528+
format = 'table'
529+
# Check the formatter used in the actual command
530+
if hasattr(cmd, 'formatter') \
531+
and cmd.formatter != cmd._formatter_plugins['table'].obj:
532+
format = 'csv'
533+
534+
sys.stdout.write('\n')
535+
targs = tparser.parse_args(['-f', format])
536+
tcmd.run(targs)
537+
505538
def interact(self):
506539
# NOTE(dtroyer): Maintain the old behaviour for interactive use as
507540
# this path does not call prepare_to_run_command()
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
#
13+
14+
"""Test Timing pseudo-command"""
15+
16+
from openstackclient.common import timing
17+
from openstackclient.tests import fakes
18+
from openstackclient.tests import utils
19+
20+
21+
timing_url = 'GET http://localhost:5000'
22+
timing_start = 1404802774.872809
23+
timing_end = 1404802775.724802
24+
25+
26+
class FakeGenericClient(object):
27+
28+
def __init__(self, **kwargs):
29+
self.auth_token = kwargs['token']
30+
self.management_url = kwargs['endpoint']
31+
32+
33+
class TestTiming(utils.TestCommand):
34+
35+
def setUp(self):
36+
super(TestTiming, self).setUp()
37+
38+
self.app.timing_data = []
39+
40+
self.app.client_manager.compute = FakeGenericClient(
41+
endpoint=fakes.AUTH_URL,
42+
token=fakes.AUTH_TOKEN,
43+
)
44+
45+
self.app.client_manager.volume = FakeGenericClient(
46+
endpoint=fakes.AUTH_URL,
47+
token=fakes.AUTH_TOKEN,
48+
)
49+
50+
# Get the command object to test
51+
self.cmd = timing.Timing(self.app, None)
52+
53+
def test_timing_list_no_data(self):
54+
arglist = []
55+
verifylist = []
56+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
57+
58+
# DisplayCommandBase.take_action() returns two tuples
59+
columns, data = self.cmd.take_action(parsed_args)
60+
61+
collist = ('URL', 'Seconds')
62+
self.assertEqual(collist, columns)
63+
datalist = [
64+
('Total', 0.0,)
65+
]
66+
self.assertEqual(datalist, data)
67+
68+
def test_timing_list(self):
69+
self.app.timing_data = [
70+
(timing_url, timing_start, timing_end),
71+
]
72+
73+
arglist = []
74+
verifylist = []
75+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
76+
77+
# DisplayCommandBase.take_action() returns two tuples
78+
columns, data = self.cmd.take_action(parsed_args)
79+
80+
collist = ('URL', 'Seconds')
81+
self.assertEqual(collist, columns)
82+
timing_sec = timing_end - timing_start
83+
datalist = [
84+
(timing_url, timing_sec),
85+
('Total', timing_sec)
86+
]
87+
self.assertEqual(datalist, data)

0 commit comments

Comments
 (0)