option to bypass managment endpoint and timings support

--timings = show timings for each call made to nova (including auth)
--bypass_url = api node endpoint to use instead of one from service catalog

For example:
nova --timings --bypass_url=http://10.24.31.37:8774/v1.1/nova-staging boot --image f304d266-0a49-4877-b34c-63aea8360297 --flavor 3 delete_me_2

Change-Id: Ib2a258b7e969ad56ce4ee2bd64c61310278cb856
This commit is contained in:
Sandy Walsh 2012-06-15 15:12:23 -03:00
parent d94edf1469
commit a2a62a5f71
4 changed files with 71 additions and 14 deletions

View File

@ -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

View File

@ -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:

View File

@ -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"):

View File

@ -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):
"""