Merge "feature: Add x-openstack-request-id into header"

This commit is contained in:
Zuul 2021-08-25 09:21:58 +00:00 committed by Gerrit Code Review
commit d182e386e9
12 changed files with 236 additions and 41 deletions

View File

@ -17,7 +17,7 @@ from __future__ import annotations
import asyncio
from typing import Any, List
from fastapi import APIRouter, HTTPException, status
from fastapi import APIRouter, Header, HTTPException, status
from skyline_log import LOG
from skyline_apiserver import schemas
@ -69,7 +69,13 @@ async def list_keystone_endpoints() -> List[schemas.ContribListKeystoneEndpoints
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def list_domains() -> Any:
async def list_domains(
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
) -> Any:
"""Contrib List Domain Names."""
try:
@ -82,7 +88,7 @@ async def list_domains() -> Any:
for region in regions:
try:
domains = await system.get_domains(region)
domains = await system.get_domains(x_openstack_request_id, region)
return [domain for domain in domains if domain not in CONF.openstack.base_domains]
except Exception as e:
LOG.warning(str(e))

View File

@ -21,7 +21,7 @@ from functools import reduce
from typing import List
from dateutil import parser
from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi import APIRouter, Depends, Header, HTTPException, Query, status
from skyline_apiserver import schemas
from skyline_apiserver.api import deps
@ -63,6 +63,11 @@ List Servers.
)
async def list_servers(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
limit: int = Query(None, gt=constants.EXTENSION_API_LIMIT_GT),
marker: str = None,
sort_dirs: schemas.ExtSortDir = None,
@ -136,6 +141,8 @@ async def list_servers(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
search_opts={"name": project_name},
)
if not filter_projects:
@ -161,6 +168,7 @@ async def list_servers(
servers = await nova.list_servers(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts=search_opts,
marker=marker,
limit=limit,
@ -195,8 +203,9 @@ async def list_servers(
tasks = [
keystone.list_projects(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
),
]
else:
@ -207,6 +216,7 @@ async def list_servers(
glance.list_images(
profile=profile,
session=system_session,
global_request_id=x_openstack_request_id,
filters={"id": "in:" + ",".join(image_ids[i : i + STEP])},
),
)
@ -216,6 +226,7 @@ async def list_servers(
cinder.list_volumes(
profile=profile,
session=cinder_session,
global_request_id=x_openstack_request_id,
search_opts={"id": root_device_ids[i : i + STEP], "all_tenants": all_projects},
),
)
@ -288,6 +299,11 @@ List Recycle Servers.
)
async def list_recycle_servers(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
limit: int = Query(None, gt=constants.EXTENSION_API_LIMIT_GT),
marker: str = None,
sort_dirs: schemas.ExtSortDir = None,
@ -351,8 +367,9 @@ async def list_recycle_servers(
if project_name:
filter_projects = await keystone.list_projects(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
search_opts={"name": project_name},
)
if not filter_projects:
@ -379,6 +396,7 @@ async def list_recycle_servers(
servers = await nova.list_servers(
profile=profile,
session=system_session,
global_request_id=x_openstack_request_id,
search_opts=search_opts,
marker=marker,
limit=limit,
@ -411,8 +429,9 @@ async def list_recycle_servers(
tasks = [
keystone.list_projects(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
),
]
else:
@ -423,6 +442,7 @@ async def list_recycle_servers(
glance.list_images(
profile=profile,
session=system_session,
global_request_id=x_openstack_request_id,
filters={"id": "in:" + ",".join(image_ids[i : i + STEP])},
),
)
@ -432,6 +452,7 @@ async def list_recycle_servers(
cinder.list_volumes(
profile=profile,
session=cinder_session,
global_request_id=x_openstack_request_id,
search_opts={"id": root_device_ids[i : i + STEP], "all_tenants": all_projects},
),
)
@ -502,6 +523,11 @@ async def list_recycle_servers(
)
async def list_volumes(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
limit: int = Query(None, gt=constants.EXTENSION_API_LIMIT_GT),
marker: str = None,
sort_dirs: schemas.ExtSortDir = None,
@ -587,6 +613,7 @@ async def list_volumes(
volumes, count = await cinder.list_volumes(
profile=profile,
session=cinder_session,
global_request_id=x_openstack_request_id,
limit=limit,
marker=marker,
search_opts=search_opts,
@ -618,8 +645,9 @@ async def list_volumes(
tasks = [
keystone.list_projects(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
),
]
else:
@ -633,6 +661,7 @@ async def list_volumes(
nova.list_servers(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts={
"uuid": server_ids[i : i + STEP],
"all_tenants": all_projects,
@ -641,6 +670,7 @@ async def list_volumes(
nova.list_servers(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts={
"uuid": server_ids[i : i + STEP],
"status": "soft_deleted",
@ -680,6 +710,11 @@ async def list_volumes(
)
async def list_volume_snapshots(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
limit: int = Query(None, gt=constants.EXTENSION_API_LIMIT_GT),
marker: str = None,
sort_dirs: schemas.ExtSortDir = None,
@ -743,6 +778,7 @@ async def list_volume_snapshots(
volume_snapshots, count = await cinder.list_volume_snapshots(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
limit=limit,
marker=marker,
search_opts=search_opts,
@ -770,8 +806,9 @@ async def list_volume_snapshots(
tasks = [
keystone.list_projects(
profile=profile,
all_projects=all_projects,
session=current_session,
global_request_id=x_openstack_request_id,
all_projects=all_projects,
),
]
else:
@ -783,6 +820,7 @@ async def list_volume_snapshots(
cinder.list_volumes(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts={"id": volume_ids[i : i + STEP], "all_tenants": all_projects},
),
)
@ -791,6 +829,7 @@ async def list_volume_snapshots(
cinder.list_volumes(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts={
"snapshot_id": snapshot_ids[i : i + STEP],
"all_tenants": all_projects,
@ -841,6 +880,11 @@ async def list_volume_snapshots(
)
async def list_ports(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
limit: int = Query(None, gt=constants.EXTENSION_API_LIMIT_GT),
marker: str = None,
sort_dirs: schemas.ExtSortDir = None,
@ -915,6 +959,7 @@ async def list_ports(
networks = await neutron.list_networks(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
**{"name": network_name},
)
if not networks["networks"]:
@ -941,7 +986,12 @@ async def list_ports(
except Exception as ex:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(ex))
ports = await get_ports(neutron_endpoint, profile.keystone_token, kwargs)
ports = await get_ports(
neutron_endpoint,
profile.keystone_token,
x_openstack_request_id,
kwargs,
)
ports_count = ports.get("count", 0)
ports = ports["ports"]
@ -959,7 +1009,12 @@ async def list_ports(
network_params = {}
tasks = [
neutron.list_networks(profile=profile, session=current_session, **{"shared": True}),
neutron.list_networks(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
**{"shared": True},
),
]
if not all_projects:
network_params["project_id"] = profile.project.id
@ -969,7 +1024,12 @@ async def list_ports(
for i in range(0, len(network_ids), STEP):
network_params["id"] = set(network_ids[i : i + STEP])
tasks.append(
neutron.list_networks(profile=profile, session=current_session, **network_params),
neutron.list_networks(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
**network_params,
),
)
# We should split the server_ids with 100 number.
@ -980,6 +1040,7 @@ async def list_ports(
nova.list_servers(
profile=profile,
session=current_session,
global_request_id=x_openstack_request_id,
search_opts={
"uuid": server_ids[i : i + STEP],
"all_tenants": all_projects,
@ -1016,6 +1077,11 @@ async def list_ports(
)
async def compute_services(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
binary: str = None,
host: str = None,
) -> schemas.ExtListComputeServicesResponse:
@ -1046,6 +1112,7 @@ async def compute_services(
services = await nova.list_services(
profile=profile,
session=system_session,
global_request_id=x_openstack_request_id,
**kwargs,
)
services = [Service(service).to_dict() for service in services]

View File

@ -14,7 +14,7 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, HTTPException, Request, Response, status
from fastapi import APIRouter, Depends, Header, HTTPException, Request, Response, status
from keystoneauth1.identity.v3 import Password
from keystoneauth1.session import Session
from keystoneclient.client import Client as KeystoneClient
@ -38,14 +38,19 @@ from skyline_apiserver.core.security import (
)
from skyline_apiserver.types import constants
from skyline_apiserver.db import api as db_api
from skyline_apiserver.types import constants
router = APIRouter()
async def _patch_profile(profile) -> schemas.Profile:
async def _patch_profile(profile: schemas.Profile, global_request_id: str) -> schemas.Profile:
try:
profile.endpoints = await get_endpoints(region=profile.region)
profile.projects = await get_projects(region=profile.region, user=profile.user.id)
profile.projects = await get_projects(
global_request_id=global_request_id,
region=profile.region,
user=profile.user.id,
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@ -65,7 +70,16 @@ async def _patch_profile(profile) -> schemas.Profile:
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def login(credential: schemas.Credential, response: Response):
async def login(
request: Request,
response: Response,
credential: schemas.Credential,
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
try:
auth_url = await utils.get_endpoint(
region=credential.region,
@ -98,7 +112,7 @@ async def login(credential: schemas.Credential, response: Response):
region=credential.region,
)
profile = await _patch_profile(profile)
profile = await _patch_profile(profile, x_openstack_request_id)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@ -120,8 +134,15 @@ async def login(credential: schemas.Credential, response: Response):
status_code=status.HTTP_200_OK,
response_description="OK",
)
async def get_profile(profile: schemas.Profile = Depends(deps.get_profile_update_jwt)):
return await _patch_profile(profile)
async def get_profile(
profile: schemas.Profile = Depends(deps.get_profile_update_jwt),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
return await _patch_profile(profile, x_openstack_request_id)
@router.post(
@ -138,13 +159,18 @@ async def logout(
response: Response,
request: Request,
payload: str = Depends(deps.getJWTPayload),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
if payload:
try:
token = parse_access_token(payload)
profile = await generate_profile_by_token(token)
session = await generate_session(profile)
await revoke_token(profile, session, token.keystone_token)
await revoke_token(profile, session, x_openstack_request_id, token.keystone_token)
await db_api.revoke_token(profile.uuid, profile.exp)
except Exception as e:
LOG.debug(str(e))
@ -167,6 +193,11 @@ async def switch_project(
project_id: str,
response: Response,
profile: schemas.Profile = Depends(deps.get_profile),
x_openstack_request_id: str = Header(
"",
alias=constants.INBOUND_HEADER,
regex=constants.INBOUND_HEADER_REGEX,
),
):
try:
project_scope_token = await get_project_scope_token(
@ -180,7 +211,7 @@ async def switch_project(
region=profile.region,
uuid_value=profile.uuid,
)
profile = await _patch_profile(profile)
profile = await _patch_profile(profile, x_openstack_request_id)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,

View File

@ -28,13 +28,18 @@ from skyline_apiserver.client import utils
async def list_volumes(
profile: schemas.Profile,
session: Session,
global_request_id: str,
limit: int = None,
marker: str = None,
search_opts: Dict[str, Any] = None,
sort: str = None,
) -> Any:
try:
cc = await utils.cinder_client(region=profile.region, session=session)
cc = await utils.cinder_client(
region=profile.region,
session=session,
global_request_id=global_request_id,
)
return await run_in_threadpool(
cc.volumes.list,
search_opts=search_opts,
@ -57,13 +62,18 @@ async def list_volumes(
async def list_volume_snapshots(
profile: schemas.Profile,
session: Session,
global_request_id: str,
limit: int = None,
marker: str = None,
search_opts: Dict[str, Any] = None,
sort: str = None,
) -> Any:
try:
cc = await utils.cinder_client(region=profile.region, session=session)
cc = await utils.cinder_client(
region=profile.region,
session=session,
global_request_id=global_request_id,
)
return await run_in_threadpool(
cc.volume_snapshots.list,
search_opts=search_opts,

View File

@ -28,13 +28,18 @@ from skyline_apiserver.client import utils
async def list_images(
profile: schemas.Profile,
session: Session,
global_request_id: str,
filters: Dict[str, Any] = None,
) -> Any:
try:
kwargs = {}
if filters:
kwargs["filters"] = filters
gc = await utils.glance_client(session=session, region=profile.region)
gc = await utils.glance_client(
session=session,
region=profile.region,
global_request_id=global_request_id,
)
return await run_in_threadpool(gc.images.list, **kwargs)
except Unauthorized as e:
raise HTTPException(

View File

@ -27,13 +27,18 @@ from skyline_apiserver.client import utils
async def list_projects(
profile: schemas.Profile,
all_projects: bool,
session: Session,
global_request_id: str,
all_projects: bool,
search_opts: Dict[str, Any] = None,
) -> Any:
try:
search_opts = search_opts if search_opts else {}
kc = await utils.keystone_client(session=session, region=profile.region)
kc = await utils.keystone_client(
session=session,
region=profile.region,
global_request_id=global_request_id,
)
if not all_projects:
search_opts["user"] = profile.user.id
return await run_in_threadpool(kc.projects.list, **search_opts)
@ -52,6 +57,7 @@ async def list_projects(
async def revoke_token(
profile: schemas.Profile,
session: Session,
global_request_id: str,
token: str,
) -> None:
"""Revoke a token.
@ -59,7 +65,11 @@ async def revoke_token(
:type token: str or :class:`keystoneclient.access.AccessInfo`
"""
try:
kc = await utils.keystone_client(session=session, region=profile.region)
kc = await utils.keystone_client(
session=session,
region=profile.region,
global_request_id=global_request_id,
)
kwargs = {"token": token}
await run_in_threadpool(kc.tokens.revoke_token, **kwargs)
except Unauthorized as e:

View File

@ -25,9 +25,18 @@ from skyline_apiserver import schemas
from skyline_apiserver.client import utils
async def list_networks(profile: schemas.Profile, session: Session, **kwargs: Any) -> Any:
async def list_networks(
profile: schemas.Profile,
session: Session,
global_request_id: str,
**kwargs: Any,
) -> Any:
try:
nc = await utils.neutron_client(session=session, region=profile.region)
nc = await utils.neutron_client(
session=session,
region=profile.region,
global_request_id=global_request_id,
)
return await run_in_threadpool(nc.list_networks, **kwargs)
except Unauthorized as e:
raise HTTPException(

View File

@ -29,6 +29,7 @@ from skyline_apiserver.client import utils
async def list_servers(
profile: schemas.Profile,
session: Session,
global_request_id: str,
search_opts: Dict[str, Any] = None,
marker: str = None,
limit: int = None,
@ -39,6 +40,7 @@ async def list_servers(
nc = await utils.nova_client(
region=profile.region,
session=session,
global_request_id=global_request_id,
)
return await run_in_threadpool(
nc.servers.list,
@ -70,9 +72,18 @@ async def list_servers(
)
async def list_services(profile: schemas.Profile, session: Session, **kwargs: Any) -> Any:
async def list_services(
profile: schemas.Profile,
session: Session,
global_request_id: str,
**kwargs: Any,
) -> Any:
try:
nc = await utils.nova_client(region=profile.region, session=session)
nc = await utils.nova_client(
region=profile.region,
session=session,
global_request_id=global_request_id,
)
return await run_in_threadpool(nc.services.list, **kwargs)
except Unauthorized as e:
raise HTTPException(

View File

@ -72,16 +72,24 @@ async def get_endpoints(region: str) -> Dict[str, Any]:
return endpoints
async def get_projects(region: str, user: str) -> Dict[str, Any]:
kc = await utils.keystone_client(session=get_system_session(), region=region)
async def get_projects(global_request_id: str, region: str, user: str) -> Dict[str, Any]:
kc = await utils.keystone_client(
session=get_system_session(),
region=region,
global_request_id=global_request_id,
)
projects = {
i.id: {"name": i.name, "domain_id": i.domain_id} for i in kc.projects.list(user=user)
}
return projects
async def get_domains(region: str) -> Any:
kc = await utils.keystone_client(session=get_system_session(), region=region)
async def get_domains(global_request_id: str, region: str) -> Any:
kc = await utils.keystone_client(
session=get_system_session(),
region=region,
global_request_id=global_request_id,
)
domains = [i.name for i in kc.domains.list(enabled=True)]
return domains

View File

@ -93,50 +93,80 @@ async def get_endpoint(region: str, service: str, session: Session) -> Any:
async def keystone_client(
session: Session,
region: str,
global_request_id: str = None,
version: str = constants.KEYSTONE_API_VERSION,
) -> HTTPClient:
endpoint = await get_endpoint(region, "keystone", session=session)
client = KeystoneClient(version=version, session=session, endpoint=endpoint)
client = KeystoneClient(
version=version,
session=session,
endpoint=endpoint,
global_request_id=global_request_id,
)
return client
async def glance_client(
session: Session,
region: str,
global_request_id: str = None,
version: str = constants.GLANCE_API_VERSION,
) -> HTTPClient:
endpoint = await get_endpoint(region, "glance", session=session)
client = GlanceClient(version=version, session=session, endpoint=endpoint)
client = GlanceClient(
version=version,
session=session,
endpoint=endpoint,
global_request_id=global_request_id,
)
return client
async def nova_client(
session: Session,
region: str,
global_request_id: str = None,
version: str = constants.NOVA_API_VERSION,
) -> HTTPClient:
endpoint = await get_endpoint(region, "nova", session=session)
client = NovaClient(version=version, session=session, endpoint_override=endpoint)
client = NovaClient(
version=version,
session=session,
endpoint_override=endpoint,
global_request_id=global_request_id,
)
return client
async def cinder_client(
session: Session,
region: str,
global_request_id: str,
version: str = constants.CINDER_API_VERSION,
) -> HTTPClient:
endpoint = await get_endpoint(region, "cinderv3", session=session)
client = CinderClient(version=version, session=session, endpoint_override=endpoint)
client = CinderClient(
version=version,
session=session,
endpoint_override=endpoint,
global_request_id=global_request_id,
)
return client
async def neutron_client(
session: Session,
region: str,
global_request_id: str = None,
version: str = constants.NEUTRON_API_VERSION,
) -> HTTPClient:
endpoint = await get_endpoint(region, "neutron", session=session)
client = NeutronClient(version=version, session=session, endpoint_override=endpoint)
client = NeutronClient(
version=version,
session=session,
endpoint_override=endpoint,
global_request_id=global_request_id,
)
return client

View File

@ -26,6 +26,7 @@ from skyline_apiserver.utils.httpclient import get_assert_200
async def get_ports(
neutron_endpoint: str,
keystone_token: str,
global_request_id: str,
search_opts: Dict[str, Any],
) -> Dict[str, Any]:
"""Get the ports in the environment .
@ -49,5 +50,8 @@ async def get_ports(
val = [v.encode("utf-8") for v in val]
qparams[opt] = val
url += "?%s" % parse.urlencode(qparams, doseq=True)
resp = await get_assert_200(url, headers={"X-Auth-Token": keystone_token})
resp = await get_assert_200(
url,
headers={"X-Auth-Token": keystone_token, constants.INBOUND_HEADER: global_request_id},
)
return resp.json()

View File

@ -21,6 +21,10 @@ CINDER_API_VERSION = "3.59"
NEUTRON_API_VERSION = "2.0"
PLACEMENT_API_VERSION = "1.36"
# request_id middleware will set this into openstack.global_request_id environ
INBOUND_HEADER = "X-Openstack-Request-Id"
INBOUND_HEADER_REGEX = "^req-\\w{8}(-\\w{4}){3}-\\w{12}"
ERR_MSG_TOKEN_REVOKED = "The token has revoked."
ERR_MSG_TOKEN_EXPIRED = "The token has expired."
ERR_MSG_TOKEN_NOTFOUND = "Token not found."