Add osprofiler option to trace operations
Add --profile option, initialize osprofiler, and pass appropriate headers to the mistral server. Change-Id: Ib10a126902c707bd82c3fadff94e119fb18cf096 Implements: blueprint mistral-osprofiler
This commit is contained in:
@@ -20,7 +20,8 @@ from mistralclient.api.v2 import client as client_v2
|
||||
def client(mistral_url=None, username=None, api_key=None,
|
||||
project_name=None, auth_url=None, project_id=None,
|
||||
endpoint_type='publicURL', service_type='workflow',
|
||||
auth_token=None, user_id=None, cacert=None, insecure=False):
|
||||
auth_token=None, user_id=None, cacert=None, insecure=False,
|
||||
profile=None):
|
||||
|
||||
if mistral_url and not isinstance(mistral_url, six.string_types):
|
||||
raise RuntimeError('Mistral url should be a string.')
|
||||
@@ -37,7 +38,8 @@ def client(mistral_url=None, username=None, api_key=None,
|
||||
auth_token=auth_token,
|
||||
user_id=user_id,
|
||||
cacert=cacert,
|
||||
insecure=insecure
|
||||
insecure=insecure,
|
||||
profile=profile
|
||||
)
|
||||
|
||||
|
||||
|
@@ -20,6 +20,8 @@ import requests
|
||||
|
||||
import logging
|
||||
|
||||
import osprofiler.web
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@@ -106,4 +108,7 @@ class HTTPClient(object):
|
||||
if user_id:
|
||||
headers['X-User-Id'] = user_id
|
||||
|
||||
# Add headers for osprofiler.
|
||||
headers.update(osprofiler.web.get_trace_id_headers())
|
||||
|
||||
return headers
|
||||
|
@@ -15,6 +15,8 @@
|
||||
|
||||
import six
|
||||
|
||||
import osprofiler.profiler
|
||||
|
||||
from mistralclient.api import httpclient
|
||||
from mistralclient.api.v2 import action_executions
|
||||
from mistralclient.api.v2 import actions
|
||||
@@ -32,7 +34,8 @@ class Client(object):
|
||||
def __init__(self, mistral_url=None, username=None, api_key=None,
|
||||
project_name=None, auth_url=None, project_id=None,
|
||||
endpoint_type='publicURL', service_type='workflowv2',
|
||||
auth_token=None, user_id=None, cacert=None, insecure=False):
|
||||
auth_token=None, user_id=None, cacert=None, insecure=False,
|
||||
profile=None):
|
||||
|
||||
if mistral_url and not isinstance(mistral_url, six.string_types):
|
||||
raise RuntimeError('Mistral url should be string')
|
||||
@@ -58,6 +61,9 @@ class Client(object):
|
||||
if not mistral_url:
|
||||
mistral_url = "http://localhost:8989/v2"
|
||||
|
||||
if profile:
|
||||
osprofiler.profiler.init(profile)
|
||||
|
||||
self.http_client = httpclient.HTTPClient(
|
||||
mistral_url,
|
||||
auth_token,
|
||||
|
@@ -142,18 +142,21 @@ class MistralShell(app.App):
|
||||
:paramtype extra_kwargs: dict
|
||||
"""
|
||||
argparse_kwargs = argparse_kwargs or {}
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=description,
|
||||
add_help=False,
|
||||
formatter_class=OpenStackHelpFormatter,
|
||||
**argparse_kwargs
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
action='version',
|
||||
version='%(prog)s {0}'.format(version),
|
||||
help='Show program\'s version number and exit.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='count',
|
||||
@@ -161,12 +164,14 @@ class MistralShell(app.App):
|
||||
default=self.DEFAULT_VERBOSE_LEVEL,
|
||||
help='Increase verbosity of output. Can be repeated.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--log-file',
|
||||
action='store',
|
||||
default=None,
|
||||
help='Specify a file to log output. Disabled by default.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-q', '--quiet',
|
||||
action='store_const',
|
||||
@@ -174,6 +179,7 @@ class MistralShell(app.App):
|
||||
const=0,
|
||||
help='Suppress output except warnings and errors.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-h', '--help',
|
||||
action=HelpAction,
|
||||
@@ -181,12 +187,14 @@ class MistralShell(app.App):
|
||||
default=self, # tricky
|
||||
help="Show this help message and exit.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Show tracebacks on errors.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-url',
|
||||
action='store',
|
||||
@@ -194,6 +202,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_MISTRAL_URL'),
|
||||
help='Mistral API host (Env: OS_MISTRAL_URL)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-version',
|
||||
action='store',
|
||||
@@ -202,6 +211,7 @@ class MistralShell(app.App):
|
||||
help='Mistral API version (default = v2) (Env: '
|
||||
'OS_MISTRAL_VERSION)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-service-type',
|
||||
action='store',
|
||||
@@ -211,6 +221,7 @@ class MistralShell(app.App):
|
||||
'keystone-endpoint) (default = workflowv2) (Env: '
|
||||
'OS_MISTRAL_SERVICE_TYPE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-endpoint-type',
|
||||
action='store',
|
||||
@@ -220,6 +231,7 @@ class MistralShell(app.App):
|
||||
'keystone-endpoint) (default = publicURL) (Env: '
|
||||
'OS_MISTRAL_ENDPOINT_TYPE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-username',
|
||||
action='store',
|
||||
@@ -227,6 +239,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_USERNAME', default='admin'),
|
||||
help='Authentication username (Env: OS_USERNAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-password',
|
||||
action='store',
|
||||
@@ -234,6 +247,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_PASSWORD'),
|
||||
help='Authentication password (Env: OS_PASSWORD)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-tenant-id',
|
||||
action='store',
|
||||
@@ -241,6 +255,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_TENANT_ID'),
|
||||
help='Authentication tenant identifier (Env: OS_TENANT_ID)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-tenant-name',
|
||||
action='store',
|
||||
@@ -248,6 +263,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_TENANT_NAME', 'Default'),
|
||||
help='Authentication tenant name (Env: OS_TENANT_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-token',
|
||||
action='store',
|
||||
@@ -255,6 +271,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_AUTH_TOKEN'),
|
||||
help='Authentication token (Env: OS_AUTH_TOKEN)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-url',
|
||||
action='store',
|
||||
@@ -262,6 +279,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_AUTH_URL'),
|
||||
help='Authentication URL (Env: OS_AUTH_URL)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cacert',
|
||||
action='store',
|
||||
@@ -269,6 +287,7 @@ class MistralShell(app.App):
|
||||
default=c.env('OS_CACERT'),
|
||||
help='Authentication CA Certificate (Env: OS_CACERT)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--insecure',
|
||||
action='store_true',
|
||||
@@ -277,6 +296,20 @@ class MistralShell(app.App):
|
||||
help='Disables SSL/TLS certificate verification '
|
||||
'(Env: MISTRALCLIENT_INSECURE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
dest='profile',
|
||||
metavar='HMAC_KEY',
|
||||
help='HMAC key to use for encrypting context data for performance '
|
||||
'profiling of operation. This key should be one of the '
|
||||
'values configured for the osprofiler middleware in mistral, '
|
||||
'it is specified in the profiler section of the mistral '
|
||||
'configuration (i.e. /etc/mistral/mistral.conf). Without the '
|
||||
'key, profiling will not be triggered even if osprofiler is '
|
||||
'enabled on the server side.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def initialize_app(self, argv):
|
||||
@@ -310,7 +343,8 @@ class MistralShell(app.App):
|
||||
service_type=self.options.service_type,
|
||||
auth_token=self.options.token,
|
||||
cacert=self.options.cacert,
|
||||
insecure=self.options.insecure
|
||||
insecure=self.options.insecure,
|
||||
profile=self.options.profile
|
||||
)
|
||||
|
||||
# Adding client_manager variable to make mistral client work with
|
||||
|
@@ -20,12 +20,15 @@ import uuid
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
import osprofiler.profiler
|
||||
|
||||
from mistralclient.api import client
|
||||
|
||||
AUTH_HTTP_URL = 'http://localhost:35357/v3'
|
||||
AUTH_HTTPS_URL = AUTH_HTTP_URL.replace('http', 'https')
|
||||
MISTRAL_HTTP_URL = 'http://localhost:8989/v2'
|
||||
MISTRAL_HTTPS_URL = MISTRAL_HTTP_URL.replace('http', 'https')
|
||||
PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
|
||||
|
||||
|
||||
class BaseClientTests(testtools.TestCase):
|
||||
@@ -175,3 +178,38 @@ class BaseClientTests(testtools.TestCase):
|
||||
os.unlink(path)
|
||||
|
||||
self.assertTrue(log_warning_mock.called)
|
||||
|
||||
@mock.patch('keystoneclient.v3.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_mistral_profile_enabled(self, mock, keystone_client_mock):
|
||||
keystone_client_instance = keystone_client_mock.return_value
|
||||
keystone_client_instance.auth_token = str(uuid.uuid4())
|
||||
keystone_client_instance.project_id = str(uuid.uuid4())
|
||||
keystone_client_instance.user_id = str(uuid.uuid4())
|
||||
|
||||
expected_args = (
|
||||
MISTRAL_HTTP_URL,
|
||||
keystone_client_instance.auth_token,
|
||||
keystone_client_instance.project_id,
|
||||
keystone_client_instance.user_id
|
||||
)
|
||||
|
||||
expected_kwargs = {
|
||||
'cacert': None,
|
||||
'insecure': False
|
||||
}
|
||||
|
||||
client.client(
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL,
|
||||
profile=PROFILER_HMAC_KEY
|
||||
)
|
||||
|
||||
self.assertTrue(mock.called)
|
||||
self.assertEqual(mock.call_args[0], expected_args)
|
||||
self.assertDictEqual(mock.call_args[1], expected_kwargs)
|
||||
|
||||
profiler = osprofiler.profiler.get()
|
||||
|
||||
self.assertEqual(profiler.hmac_key, PROFILER_HMAC_KEY)
|
||||
|
@@ -19,6 +19,9 @@ import mock
|
||||
import requests
|
||||
import testtools
|
||||
|
||||
from osprofiler import _utils as osprofiler_utils
|
||||
import osprofiler.profiler
|
||||
|
||||
from mistralclient.api import httpclient
|
||||
|
||||
API_BASE_URL = 'http://localhost:8989/v2'
|
||||
@@ -29,6 +32,8 @@ EXPECTED_URL = API_BASE_URL + API_URL
|
||||
AUTH_TOKEN = str(uuid.uuid4())
|
||||
PROJECT_ID = str(uuid.uuid4())
|
||||
USER_ID = str(uuid.uuid4())
|
||||
PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
|
||||
PROFILER_TRACE_ID = str(uuid.uuid4())
|
||||
|
||||
EXPECTED_AUTH_HEADERS = {
|
||||
'x-auth-token': AUTH_TOKEN,
|
||||
@@ -65,6 +70,7 @@ class HTTPClientTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HTTPClientTest, self).setUp()
|
||||
osprofiler.profiler.init(None)
|
||||
self.client = httpclient.HTTPClient(
|
||||
API_BASE_URL,
|
||||
AUTH_TOKEN,
|
||||
@@ -103,6 +109,42 @@ class HTTPClientTest(testtools.TestCase):
|
||||
**expected_options
|
||||
)
|
||||
|
||||
@mock.patch.object(
|
||||
osprofiler.profiler._Profiler,
|
||||
'get_base_id',
|
||||
mock.MagicMock(return_value=PROFILER_TRACE_ID)
|
||||
)
|
||||
@mock.patch.object(
|
||||
osprofiler.profiler._Profiler,
|
||||
'get_id',
|
||||
mock.MagicMock(return_value=PROFILER_TRACE_ID)
|
||||
)
|
||||
@mock.patch.object(
|
||||
requests,
|
||||
'get',
|
||||
mock.MagicMock(return_value=FakeResponse('get', EXPECTED_URL, 200))
|
||||
)
|
||||
def test_get_request_options_with_profile_enabled(self):
|
||||
osprofiler.profiler.init(PROFILER_HMAC_KEY)
|
||||
|
||||
data = {'base_id': PROFILER_TRACE_ID, 'parent_id': PROFILER_TRACE_ID}
|
||||
signed_data = osprofiler_utils.signed_pack(data, PROFILER_HMAC_KEY)
|
||||
|
||||
headers = {
|
||||
'X-Trace-Info': signed_data[0],
|
||||
'X-Trace-HMAC': signed_data[1]
|
||||
}
|
||||
|
||||
self.client.get(API_URL)
|
||||
|
||||
expected_options = copy.deepcopy(EXPECTED_REQ_OPTIONS)
|
||||
expected_options['headers'].update(headers)
|
||||
|
||||
requests.get.assert_called_with(
|
||||
EXPECTED_URL,
|
||||
**expected_options
|
||||
)
|
||||
|
||||
@mock.patch.object(
|
||||
requests,
|
||||
'post',
|
||||
|
@@ -109,3 +109,10 @@ class TestShell(base.BaseShellTests):
|
||||
self.assertTrue(mock.called)
|
||||
params = mock.call_args
|
||||
self.assertEqual('http://localhost:35357/v3', params[1]['auth_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_profile(self, mock):
|
||||
self.shell('--profile=SECRET_HMAC_KEY workbook-list')
|
||||
self.assertTrue(mock.called)
|
||||
params = mock.call_args
|
||||
self.assertEqual('SECRET_HMAC_KEY', params[1]['profile'])
|
||||
|
@@ -2,6 +2,7 @@
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
cliff!=1.16.0,!=1.17.0,>=1.15.0 # Apache-2.0
|
||||
osprofiler>=1.3.0 # Apache-2.0
|
||||
pbr>=1.6 # Apache-2.0
|
||||
python-keystoneclient!=1.8.0,!=2.1.0,>=1.7.0 # Apache-2.0
|
||||
python-openstackclient>=2.1.0 # Apache-2.0
|
||||
|
Reference in New Issue
Block a user