Send global_request_id for tracing calls

This patch adds global_request_id to the constructor for nova client,
keystone client, neutron client and placement client which will pass the
global_request_id to these respective services on all API calls.
Supporting global_request_id makes debugging easier when requests reach
many different OpenStack services. The blazar global_request_id will be
sent to these services in the request header like below and it will be
available with context.global_request_id:

  -H "X-OpenStack-Request-ID: req-1a9b7b24-02ed-4400-bcc3-cc1bcbb59147"

Sample log output of neutron service for ``POST v1/floatingips`` API
which logs global request ID `req-e19f8f4f-40e7-441e-b776-7b43ed15c7dd`
is shown at: http://paste.openstack.org/show/753807

Oslo spec I65de8261746b25d45e105394f4eeb95b9cb3bd42
Change-Id: I5bb3631c4fb178293ee8eefbe1aa8b819a196a9f
This commit is contained in:
asmita singh 2019-06-27 08:54:03 +00:00 committed by Pierre Riteau
parent bd8185e694
commit 563e4a8da2
15 changed files with 107 additions and 15 deletions

View File

@ -0,0 +1,25 @@
.. -*- rst -*-
==================
Global Request ID
==================
Users can specify a global request ID as a request header.
**Request**
.. rest_parameters:: parameters.yaml
- X-Openstack-Request-Id: x-openstack-request-id_req
**Request Header**
In each REST API request, you can specify a global request ID
in the ``X-Openstack-Request-Id`` header.
The format must be ``req-`` + UUID (UUID4).
If not in accordance with the format, the global request ID is
ignored by Blazar.
Request header example::
X-Openstack-Request-Id: req-e19f8f4f-40e7-441e-b776-7b43ed15c7dd

View File

@ -11,3 +11,4 @@ Blazar project.
.. include:: hosts.inc
.. include:: floatingips.inc
.. include:: request-ids.inc
.. include:: global-request-id.inc

View File

@ -1,4 +1,17 @@
# variables in headers
x-openstack-request-id_req:
description: |
The global request ID, which is a unique common ID
for tracking each request in OpenStack services.
The format of the global request ID must be ``req-`` + UUID (UUID4).
If not in accordance with the format, it is ignored.
It is associated with the request and appears in the log lines
for that request.
The global request ID appears in the Blazar logs and all cross
services it interacts with.
in: header
required: false
type: string
x-openstack-request-id_resp:
description: |
The local request ID, which is a unique ID generated automatically

View File

@ -40,4 +40,6 @@ def ctx_from_headers(headers):
# For v1 only, request_id and global_request_id will be available.
if headers.environ['PATH_INFO'].startswith('/v1'):
kwargs['request_id'] = headers.environ['openstack.request_id']
kwargs['global_request_id'] = headers.environ.get(
'openstack.global_request_id')
return context.BlazarContext(**kwargs)

View File

@ -58,7 +58,8 @@ class ContextTestCaseV1(ContextTestCase):
def test_ctx_from_headers(self):
self.fake_headers[u'X-Service-Catalog'] = self.catalog
environ_base = {
'openstack.request_id': 'req-' + uuidsentinel.reqid}
'openstack.request_id': 'req-' + uuidsentinel.reqid,
'openstack.global_request_id': 'req-' + uuidsentinel.globalreqid}
req = wrappers.Request.from_values(
'/v1/leases',
headers=self.fake_headers,
@ -74,7 +75,8 @@ class ContextTestCaseV1(ContextTestCase):
service_catalog={u'nova': u'catalog'},
project_id=uuidsentinel.project_id,
user_name=u'user_name',
request_id='req-' + uuidsentinel.reqid)
request_id='req-' + uuidsentinel.reqid,
global_request_id='req-' + uuidsentinel.globalreqid)
class ContextTestCaseV2(ContextTestCase):

View File

@ -74,7 +74,10 @@ class TestCKClient(tests.TestCase):
endpoint='http://fake.com/',
username=self.username,
password=self.password,
auth_url=self.auth_url)
auth_url=self.auth_url,
global_request_id=self.
context.current().
global_request_id)
def test_client_from_ctx(self):
@ -86,7 +89,8 @@ class TestCKClient(tests.TestCase):
token=self.ctx().auth_token,
tenant_name=self.ctx().project_name,
auth_url='http://fake.com/',
endpoint='http://fake.com/')
endpoint='http://fake.com/',
global_request_id=self.context.current().global_request_id)
def test_complement_auth_url_supported_api_version(self):
bkc = self.keystone.BlazarKeystoneClient()

View File

@ -19,6 +19,7 @@ from neutronclient.common import exceptions as neutron_exceptions
from oslo_config import cfg
from oslo_config import fixture
from blazar import context
from blazar import tests
from blazar.utils.openstack import exceptions
from blazar.utils.openstack import neutron
@ -30,16 +31,21 @@ class TestBlazarNeutronClient(tests.TestCase):
def setUp(self):
super(TestBlazarNeutronClient, self).setUp()
self.cfg = self.useFixture(fixture.Config(CONF))
self.context = context
self.ctx = self.patch(self.context, 'current')
def test_client_from_kwargs(self):
kwargs = {
'auth_url': 'http://foo:8080/identity/v3',
'region_name': 'RegionTwo'
'region_name': 'RegionTwo',
'global_request_id': 'req-e19f8f4f-40e7-441e-b776-7b43ed15c7dd'
}
client = neutron.BlazarNeutronClient(**kwargs)
self.assertEqual("http://foo:8080/identity/v3",
client.neutron.httpclient.session.auth.auth_url)
self.assertEqual("RegionTwo", client.neutron.httpclient.region_name)
self.assertEqual("req-e19f8f4f-40e7-441e-b776-7b43ed15c7dd",
client.neutron.httpclient.global_request_id)
class TestFloatingIPPool(tests.TestCase):

View File

@ -94,7 +94,8 @@ class TestCNClient(tests.TestCase):
self.session.assert_called_once_with(auth=self.auth.return_value)
self.client.assert_called_once_with(version=self.version,
endpoint_override=self.url,
session=self.session.return_value)
session=self.session.return_value,
global_request_id=mock.ANY)
def test_getattr(self):
# TODO(n.s.): Will be done as soon as pypi package will be updated

View File

@ -62,6 +62,7 @@ class TestTrusts(tests.TestCase):
fake_ctx_dict = {
'auth_token': self.client().auth_token,
'domain': None,
'global_request_id': self.context.current().global_request_id,
'is_admin': False,
'is_admin_project': True,
'project': self.client().tenant_id,

View File

@ -102,6 +102,7 @@ class BlazarKeystoneClient(object):
if ctx is not None:
kwargs.setdefault('username', ctx.user_name)
kwargs.setdefault('tenant_name', ctx.project_name)
kwargs.setdefault('global_request_id', ctx.global_request_id)
if not kwargs.get('auth_url'):
kwargs['auth_url'] = base.url_for(
ctx.service_catalog, CONF.identity_service,

View File

@ -22,6 +22,7 @@ from neutronclient.v2_0 import client as neutron_client
from oslo_config import cfg
from oslo_log import log as logging
from blazar import context
from blazar.utils.openstack import exceptions
CONF = cfg.CONF
@ -32,6 +33,7 @@ class BlazarNeutronClient(object):
"""Client class for Neutron service."""
def __init__(self, **kwargs):
ctx = kwargs.pop('ctx', None)
username = kwargs.pop('username',
CONF.os_admin_username)
password = kwargs.pop('password',
@ -44,6 +46,13 @@ class BlazarNeutronClient(object):
CONF.os_admin_project_domain_name)
auth_url = kwargs.pop('auth_url', None)
region_name = kwargs.pop('region_name', CONF.os_region_name)
if ctx is None:
try:
ctx = context.current()
except RuntimeError:
pass
if ctx is not None:
kwargs.setdefault('global_request_id', ctx.global_request_id)
if auth_url is None:
auth_url = "%s://%s:%s/%s/%s" % (CONF.os_auth_protocol,
@ -59,8 +68,9 @@ class BlazarNeutronClient(object):
user_domain_name=user_domain_name,
project_domain_name=project_domain_name)
sess = session.Session(auth=auth)
self.neutron = neutron_client.Client(
session=sess, region_name=region_name)
kwargs.setdefault('session', sess)
kwargs.setdefault('region_name', region_name)
self.neutron = neutron_client.Client(**kwargs)
class FloatingIPPool(BlazarNeutronClient):

View File

@ -127,6 +127,7 @@ class BlazarNovaClient(object):
os_region_name=CONF.os_region_name)
auth_url = base.url_for(ctx.service_catalog, CONF.identity_service,
os_region_name=CONF.os_region_name)
kwargs.setdefault('global_request_id', ctx.global_request_id)
if auth_url is None:
auth_url = "%s://%s:%s/%s" % (CONF.os_auth_protocol,

View File

@ -20,6 +20,7 @@ from keystoneauth1 import session
from oslo_config import cfg
from oslo_log import log as logging
from blazar import context
from blazar.utils.openstack import exceptions
CONF = cfg.CONF
@ -33,6 +34,7 @@ class BlazarPlacementClient(object):
def _create_client(self, **kwargs):
"""Create the HTTP session accessing the placement service."""
ctx = kwargs.pop('ctx', None)
username = kwargs.pop('username',
CONF.os_admin_username)
user_domain_name = kwargs.pop('user_domain_name',
@ -47,6 +49,14 @@ class BlazarPlacementClient(object):
auth_url = kwargs.pop('auth_url', None)
region_name = kwargs.pop('region_name', CONF.os_region_name)
if ctx is None:
try:
ctx = context.current()
except RuntimeError:
pass
if ctx is not None:
kwargs.setdefault('global_request_id', ctx.global_request_id)
if auth_url is None:
auth_url = "%s://%s:%s/%s/%s" % (CONF.os_auth_protocol,
CONF.os_auth_host,
@ -64,11 +74,11 @@ class BlazarPlacementClient(object):
# Set accept header on every request to ensure we notify placement
# service of our response body media type preferences.
headers = {'accept': 'application/json'}
client = adapter.Adapter(session=sess,
service_type='placement',
interface='public',
region_name=region_name,
additional_headers=headers)
kwargs.setdefault('service_type', 'placement')
kwargs.setdefault('interface', 'public')
kwargs.setdefault('additional_headers', headers)
kwargs.setdefault('region_name', region_name)
client = adapter.Adapter(sess, **kwargs)
return client
def get(self, url, microversion=PLACEMENT_MICROVERSION):

View File

@ -55,7 +55,8 @@ def create_ctx_from_trust(trust_id):
ctx = context.BlazarContext(
user_name=CONF.os_admin_username,
project_name=CONF.os_admin_project_name,
request_id=ctx.request_id
request_id=ctx.request_id,
global_request_id=ctx.global_request_id
)
auth_url = "%s://%s:%s/%s" % (CONF.os_auth_protocol,
CONF.os_auth_host,
@ -75,7 +76,8 @@ def create_ctx_from_trust(trust_id):
auth_token=client.auth_token,
service_catalog=client.service_catalog.catalog['catalog'],
project_id=client.tenant_id,
request_id=ctx.request_id
request_id=ctx.request_id,
global_request_id=ctx.global_request_id
)

View File

@ -0,0 +1,13 @@
---
features:
- |
Adds support for `global_request_id`_ to the ``RequestId`` middleware. An
inbound header of ``X-OpenStack-Request-ID`` is accepted as long as it is
of the format ``req-$uuid``, and made available to oslo.context. This
allows for cross-project request ID tracking. By default, global request
IDs will not appear in the Blazar service logs. Operators need to add
global_request_id in the `logging_context_format_string`_ configuration
option.
.. _`global_request_id`: https://developer.openstack.org/api-ref/reservation/v1/index.html#global-request-id
.. _`logging_context_format_string`: https://docs.openstack.org/oslo.log/latest/configuration/index.html#DEFAULT.logging_context_format_string