diff --git a/novaclient/client.py b/novaclient/client.py index 8c703117b..31d6ebe57 100644 --- a/novaclient/client.py +++ b/novaclient/client.py @@ -10,6 +10,7 @@ OpenStack Client interface. Handles the REST calls and responses. import httplib2 import logging import os +import time import urlparse try: @@ -38,7 +39,8 @@ class HTTPClient(httplib2.Http): timeout=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', service_type=None, - service_name=None, volume_service_name=None): + service_name=None, volume_service_name=None, + timings=False): super(HTTPClient, self).__init__(timeout=timeout) self.user = user self.password = password @@ -50,6 +52,9 @@ class HTTPClient(httplib2.Http): self.service_type = service_type self.service_name = service_name self.volume_service_name = volume_service_name + self.timings = timings + + self.times = [] # [("item", starttime, endtime), ...] self.management_url = None self.auth_token = None @@ -60,6 +65,12 @@ class HTTPClient(httplib2.Http): self.force_exception_to_status_code = True self.disable_ssl_certificate_validation = insecure + def set_management_url(self, url): + self.management_url = url + + def get_timings(self): + return self.times + def http_log(self, args, kwargs, resp, body): if not _logger.isEnabledFor(logging.DEBUG): return @@ -105,6 +116,13 @@ class HTTPClient(httplib2.Http): return resp, body + def _time_request(self, url, method, **kwargs): + start_time = time.time() + resp, body = self.request(url, method, **kwargs) + self.times.append(("%s %s" % (method, url), + start_time, time.time())) + return resp, body + def _cs_request(self, url, method, **kwargs): if not self.management_url: self.authenticate() @@ -117,14 +135,14 @@ class HTTPClient(httplib2.Http): if self.projectid: kwargs['headers']['X-Auth-Project-Id'] = self.projectid - resp, body = self.request(self.management_url + url, method, + resp, body = self._time_request(self.management_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized, ex: try: self.authenticate() - resp, body = self.request(self.management_url + url, method, - **kwargs) + resp, body = self._time_request(self.management_url + url, + method, **kwargs) return resp, body except exceptions.Unauthorized: raise ex @@ -195,7 +213,7 @@ class HTTPClient(httplib2.Http): url = '/'.join([url, 'tokens', '%s?belongsTo=%s' % (self.proxy_token, self.proxy_tenant_id)]) _logger.debug("Using Endpoint URL: %s" % url) - resp, body = self.request(url, "GET", + resp, body = self._time_request(url, "GET", headers={'X-Auth_Token': self.auth_token}) return self._extract_service_catalog(url, resp, body, extract_token=False) @@ -256,7 +274,7 @@ class HTTPClient(httplib2.Http): if self.projectid: headers['X-Auth-Project-Id'] = self.projectid - resp, body = self.request(url, 'GET', headers=headers) + resp, body = self._time_request(url, 'GET', headers=headers) if resp.status in (200, 204): # in some cases we get No Content try: mgmt_header = 'x-server-management-url' @@ -300,7 +318,7 @@ class HTTPClient(httplib2.Http): self.follow_all_redirects = True try: - resp, body = self.request(token_url, "POST", body=body) + resp, body = self._time_request(token_url, "POST", body=body) finally: self.follow_all_redirects = tmp_follow_all_redirects diff --git a/novaclient/shell.py b/novaclient/shell.py index 76509d8f6..3c905d159 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -86,6 +86,11 @@ class OpenStackComputeShell(object): action='store_true', help="Print debugging output") + parser.add_argument('--timings', + default=False, + action='store_true', + help="Print call timing info") + parser.add_argument('--os_username', default=utils.env('OS_USERNAME', 'NOVA_USERNAME'), help='Defaults to env[OS_USERNAME].') @@ -159,6 +164,9 @@ class OpenStackComputeShell(object): default=utils.env('NOVA_URL'), help='Deprecated') + parser.add_argument('--bypass_url', dest='bypass_url', + help="Use this API endpoint instead of the Service Catalog") + return parser def get_subcommand_parser(self, version): @@ -298,14 +306,16 @@ class OpenStackComputeShell(object): (os_username, os_password, os_tenant_name, os_auth_url, os_region_name, endpoint_type, insecure, service_type, service_name, volume_service_name, - username, apikey, projectid, url, region_name) = ( + username, apikey, projectid, url, region_name, + bypass_url) = ( args.os_username, args.os_password, args.os_tenant_name, args.os_auth_url, args.os_region_name, args.endpoint_type, args.insecure, args.service_type, args.service_name, args.volume_service_name, args.username, args.apikey, args.projectid, - args.url, args.region_name) + args.url, args.region_name, + args.bypass_url) if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE @@ -366,11 +376,14 @@ class OpenStackComputeShell(object): region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, - volume_service_name=volume_service_name) + volume_service_name=volume_service_name, + timings=args.timings) try: if not utils.isunauthenticated(args.func): self.cs.authenticate() + if bypass_url: + self.cs.set_management_url(bypass_url) except exc.Unauthorized: raise exc.CommandError("Invalid OpenStack Nova credentials.") except exc.AuthorizationFailure: @@ -378,6 +391,21 @@ class OpenStackComputeShell(object): args.func(self.cs, args) + if args.timings: + self._dump_timings(self.cs.get_timings()) + + def _dump_timings(self, timings): + class Tyme(object): + def __init__(self, url, seconds): + self.url = url + self.seconds = seconds + results = [Tyme(url, end - start) for url, start, end in timings] + total = 0.0 + for tyme in results: + total += tyme.seconds + results.append(Tyme("Total", total)) + utils.print_list(results, ["url", "seconds"], sortby_index=None) + def _run_extension_hooks(self, hook_type, *args, **kwargs): """Run hooks for all registered extensions.""" for extension in self.extensions: diff --git a/novaclient/utils.py b/novaclient/utils.py index a8e998a18..058d400b4 100644 --- a/novaclient/utils.py +++ b/novaclient/utils.py @@ -123,7 +123,11 @@ def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) -def print_list(objs, fields, formatters={}): +def print_list(objs, fields, formatters={}, sortby_index=0): + if sortby_index == None: + sortby = None + else: + sortby = fields[sortby_index] mixed_case_fields = ['serverId'] pt = prettytable.PrettyTable([f for f in fields], caching=False) pt.align = 'l' @@ -142,7 +146,7 @@ def print_list(objs, fields, formatters={}): row.append(data) pt.add_row(row) - print pt.get_string(sortby=fields[0]) + print pt.get_string(sortby=sortby) def print_dict(d, property="Property"): diff --git a/novaclient/v1_1/client.py b/novaclient/v1_1/client.py index 047080c54..cc54599ec 100644 --- a/novaclient/v1_1/client.py +++ b/novaclient/v1_1/client.py @@ -45,7 +45,7 @@ class Client(object): proxy_token=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type='compute', service_name=None, - volume_service_name=None): + volume_service_name=None, timings=False): # FIXME(comstud): Rename the api_key argument above when we # know it's not being used as keyword argument password = api_key @@ -95,7 +95,14 @@ class Client(object): endpoint_type=endpoint_type, service_type=service_type, service_name=service_name, - volume_service_name=volume_service_name) + volume_service_name=volume_service_name, + timings=timings) + + def set_management_url(self, url): + self.client.management_url = url + + def get_timings(self): + return self.client.get_timings() def authenticate(self): """