Merge "Add basic timing support"
This commit is contained in:
commit
bc6495c6a1
@ -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)
|
||||
),
|
||||
|
44
openstackclient/common/timing.py
Normal file
44
openstackclient/common/timing.py
Normal file
@ -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,
|
||||
)
|
@ -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:
|
||||
|
@ -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()
|
||||
|
87
openstackclient/tests/common/test_timing.py
Normal file
87
openstackclient/tests/common/test_timing.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user