Skip to content

Commit 0d68987

Browse files
Dean Troyerlin-hua-cheng
authored andcommitted
Fix session timing
Subclass keystoneclient.session.Session to add the timing hooks to record the elapsed time returned by requests.Response objects, including the redirection history. Redirects are included individually and not rolled into the total time for the original request. This works for all clients that use OSC's session. Closes-Bug: #1402577 Change-Id: I9360c90c151579b89a37edb8c11c17feb15b3cb9
1 parent a9d1e3d commit 0d68987

5 files changed

Lines changed: 71 additions & 19 deletions

File tree

‎openstackclient/common/clientmanager.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
import pkg_resources
2020
import sys
2121

22-
from keystoneclient import session
2322
import requests
2423

2524
from openstackclient.api import auth
25+
from openstackclient.common import session as osc_session
2626
from openstackclient.identity import client as identity_client
2727

2828

@@ -157,7 +157,7 @@ def setup_auth(self):
157157
self.auth = auth_plugin.load_from_options(**self._auth_params)
158158
# needed by SAML authentication
159159
request_session = requests.session()
160-
self.session = session.Session(
160+
self.session = osc_session.TimingSession(
161161
auth=self.auth,
162162
session=request_session,
163163
verify=self._verify,

‎openstackclient/common/session.py‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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+
"""Subclass of keystoneclient.session"""
15+
16+
from keystoneclient import session
17+
18+
19+
class TimingSession(session.Session):
20+
"""A Session that supports collection of timing data per Method URL"""
21+
22+
def __init__(
23+
self,
24+
**kwargs
25+
):
26+
"""Pass through all arguments except timing"""
27+
super(TimingSession, self).__init__(**kwargs)
28+
29+
# times is a list of tuples: ("method url", elapsed_time)
30+
self.times = []
31+
32+
def get_timings(self):
33+
return self.times
34+
35+
def reset_timings(self):
36+
self.times = []
37+
38+
def request(self, url, method, **kwargs):
39+
"""Wrap the usual request() method with the timers"""
40+
resp = super(TimingSession, self).request(url, method, **kwargs)
41+
for h in resp.history:
42+
self.times.append((
43+
"%s %s" % (h.request.method, h.request.url),
44+
h.elapsed,
45+
))
46+
self.times.append((
47+
"%s %s" % (resp.request.method, resp.request.url),
48+
resp.elapsed,
49+
))
50+
return resp

‎openstackclient/common/timing.py‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ def take_action(self, parsed_args):
3333

3434
results = []
3535
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))
36+
for url, td in self.app.timing_data:
37+
# NOTE(dtroyer): Take the long way here because total_seconds()
38+
# was added in py27.
39+
sec = (td.microseconds + (td.seconds + td.days*86400) * 1e6) / 1e6
40+
total += sec
41+
results.append((url, sec))
4042
results.append(('Total', total))
4143
return (
4244
column_headers,

‎openstackclient/shell.py‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,10 @@ def clean_up(self, cmd, result, err):
316316

317317
# Process collected timing data
318318
if self.options.timing:
319-
# Loop through extensions
320-
for mod in self.ext_modules:
321-
client = getattr(self.client_manager, mod.API_NAME)
322-
if hasattr(client, 'get_timings'):
323-
self.timing_data.extend(client.get_timings())
319+
# Get session data
320+
self.timing_data.extend(
321+
self.client_manager.session.get_timings(),
322+
)
324323

325324
# Use the Timing pseudo-command to generate the output
326325
tcmd = timing.Timing(self, self.options)

‎openstackclient/tests/common/test_timing.py‎

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313

1414
"""Test Timing pseudo-command"""
1515

16+
import datetime
17+
1618
from openstackclient.common import timing
1719
from openstackclient.tests import fakes
1820
from openstackclient.tests import utils
1921

2022

2123
timing_url = 'GET http://localhost:5000'
22-
timing_start = 1404802774.872809
23-
timing_end = 1404802775.724802
24+
timing_elapsed = 0.872809
2425

2526

2627
class FakeGenericClient(object):
@@ -66,9 +67,10 @@ def test_timing_list_no_data(self):
6667
self.assertEqual(datalist, data)
6768

6869
def test_timing_list(self):
69-
self.app.timing_data = [
70-
(timing_url, timing_start, timing_end),
71-
]
70+
self.app.timing_data = [(
71+
timing_url,
72+
datetime.timedelta(microseconds=timing_elapsed*1000000),
73+
)]
7274

7375
arglist = []
7476
verifylist = []
@@ -79,9 +81,8 @@ def test_timing_list(self):
7981

8082
collist = ('URL', 'Seconds')
8183
self.assertEqual(collist, columns)
82-
timing_sec = timing_end - timing_start
8384
datalist = [
84-
(timing_url, timing_sec),
85-
('Total', timing_sec)
85+
(timing_url, timing_elapsed),
86+
('Total', timing_elapsed),
8687
]
8788
self.assertEqual(datalist, data)

0 commit comments

Comments
 (0)