feature: Add fixed ip filter in list_servers
Change-Id: I846f17ab2c27ffecc2c262eca07546eb0504b2c7 Signed-off-by: Wu Wenxiang <wu.wenxiang@99cloud.net>
This commit is contained in:
@@ -115,6 +115,13 @@ def list_servers(
|
||||
None, description="Filter the list of servers by the given flavor ID."
|
||||
),
|
||||
uuid: str = Query(None, description="Filter the list of servers by the given server UUID."),
|
||||
ip: Optional[str] = Query(
|
||||
None,
|
||||
description=(
|
||||
"Filter the list of servers by the given IP address (only fixed, not floating). "
|
||||
"Also passed to Nova API if supported."
|
||||
),
|
||||
),
|
||||
) -> schemas.ServersResponse:
|
||||
all_projects = all_projects or False
|
||||
if all_projects:
|
||||
@@ -158,6 +165,8 @@ def list_servers(
|
||||
"all_tenants": all_projects,
|
||||
"uuid": uuid,
|
||||
}
|
||||
if ip is not None:
|
||||
search_opts["ip"] = ip
|
||||
servers = nova.list_servers(
|
||||
profile=profile,
|
||||
session=current_session,
|
||||
|
@@ -20,7 +20,8 @@ from pathlib import Path
|
||||
from typing import AsyncGenerator
|
||||
|
||||
import jose
|
||||
from fastapi import FastAPI, Request, Response, status
|
||||
from fastapi import FastAPI, Request, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
from skyline_apiserver.api.v1 import api_router
|
||||
@@ -93,8 +94,9 @@ async def validate_token(request: Request, call_next):
|
||||
# Get token from cookie
|
||||
token = request.cookies.get(CONF.default.session_name)
|
||||
if not token:
|
||||
return Response(
|
||||
content="Unauthorized: Token not found", status_code=status.HTTP_401_UNAUTHORIZED
|
||||
return JSONResponse(
|
||||
content={"message": "Unauthorized: Token not found"},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -105,8 +107,8 @@ async def validate_token(request: Request, call_next):
|
||||
parsed_token = parse_access_token(token)
|
||||
is_revoked = db_api.check_token(parsed_token.uuid)
|
||||
if is_revoked:
|
||||
return Response(
|
||||
content="Unauthorized: Token revoked",
|
||||
return JSONResponse(
|
||||
content={"message": "Unauthorized: Token revoked"},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
@@ -136,13 +138,14 @@ async def validate_token(request: Request, call_next):
|
||||
request.state.new_exp = str(profile.exp)
|
||||
|
||||
except jose.exceptions.ExpiredSignatureError as e:
|
||||
return Response(
|
||||
content=f"Unauthorized: Token expired - {str(e)}",
|
||||
return JSONResponse(
|
||||
content={"message": f"Unauthorized: Token expired - {str(e)}"},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
except Exception as e:
|
||||
return Response(
|
||||
content=f"Unauthorized: {str(e)}", status_code=status.HTTP_401_UNAUTHORIZED
|
||||
return JSONResponse(
|
||||
content={"message": f"Unauthorized: {str(e)}"},
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
)
|
||||
|
||||
response = await call_next(request)
|
||||
|
@@ -245,3 +245,108 @@ class TestListVolumesReal:
|
||||
assert hasattr(result, "count")
|
||||
mock_cinder.list_volumes.assert_called_once()
|
||||
mock_nova.list_servers.assert_called()
|
||||
|
||||
|
||||
class TestListServersReal:
|
||||
"""Real test cases for list_servers function"""
|
||||
|
||||
@pytest.fixture
|
||||
def mock_profile(self):
|
||||
profile = Mock()
|
||||
profile.project.id = "test-project-id"
|
||||
profile.project.name = "test-project"
|
||||
profile.region = "test-region"
|
||||
return profile
|
||||
|
||||
@pytest.fixture
|
||||
def mock_server_data(self):
|
||||
return {
|
||||
"id": "server-1",
|
||||
"name": "vm-1",
|
||||
"image": None,
|
||||
"volumes_attached": [],
|
||||
"project_id": "test-project-id",
|
||||
}
|
||||
|
||||
@patch("skyline_apiserver.api.v1.extension.nova")
|
||||
@patch("skyline_apiserver.api.v1.extension.glance")
|
||||
@patch("skyline_apiserver.api.v1.extension.cinder")
|
||||
@patch("skyline_apiserver.api.v1.extension.keystone")
|
||||
@patch("skyline_apiserver.api.v1.extension.generate_session")
|
||||
@patch("skyline_apiserver.api.v1.extension.get_system_session")
|
||||
@patch("skyline_apiserver.api.v1.extension.OSServer")
|
||||
@patch("skyline_apiserver.api.v1.extension.Server")
|
||||
@patch("skyline_apiserver.api.v1.extension.schemas")
|
||||
def test_list_servers_search_opts(
|
||||
self,
|
||||
mock_schemas,
|
||||
mock_server_wrapper,
|
||||
mock_osserver_wrapper,
|
||||
mock_get_system_session,
|
||||
mock_generate_session,
|
||||
mock_keystone,
|
||||
mock_cinder,
|
||||
mock_glance,
|
||||
mock_nova,
|
||||
mock_profile,
|
||||
mock_server_data,
|
||||
):
|
||||
# Setup sessions
|
||||
mock_system_session = Mock()
|
||||
mock_current_session = Mock()
|
||||
mock_get_system_session.return_value = mock_system_session
|
||||
mock_generate_session.return_value = mock_current_session
|
||||
|
||||
# Mock nova.list_servers
|
||||
server_obj = Mock()
|
||||
mock_nova.list_servers.return_value = [server_obj]
|
||||
|
||||
# Mock wrappers
|
||||
server_wrapper_obj = Mock()
|
||||
server_wrapper_obj.to_dict.return_value = mock_server_data
|
||||
mock_server_wrapper.return_value = server_wrapper_obj
|
||||
|
||||
osserver_wrapper_obj = Mock()
|
||||
osserver_wrapper_obj.to_dict.return_value = mock_server_data
|
||||
mock_osserver_wrapper.return_value = osserver_wrapper_obj
|
||||
|
||||
# Mock schemas.ServersResponse
|
||||
response_obj = Mock()
|
||||
response_obj.servers = [mock_server_data]
|
||||
mock_schemas.ServersResponse.return_value = response_obj
|
||||
|
||||
# Import target after patches are ready
|
||||
from skyline_apiserver.api.v1.extension import list_servers
|
||||
|
||||
# Call function with a set of filters to verify passthrough
|
||||
result = list_servers(
|
||||
profile=mock_profile,
|
||||
x_openstack_request_id="req-1",
|
||||
all_projects=False,
|
||||
limit=None,
|
||||
marker=None,
|
||||
sort_dirs=None,
|
||||
sort_keys=[],
|
||||
project_id="should-be-ignored",
|
||||
project_name=None,
|
||||
name="vm-1",
|
||||
status=None,
|
||||
host="compute-1",
|
||||
flavor_id="flavor-1",
|
||||
uuid="uuid-1",
|
||||
ip="10.0.0.5",
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
mock_nova.list_servers.assert_called_once()
|
||||
call_args = mock_nova.list_servers.call_args
|
||||
assert call_args[1]["session"] == mock_current_session
|
||||
search_opts = call_args[1]["search_opts"]
|
||||
# project_id is ignored when all_projects is False
|
||||
assert search_opts["project_id"] is None
|
||||
assert search_opts["all_tenants"] is False
|
||||
assert search_opts["name"] == "vm-1"
|
||||
assert search_opts["host"] == "compute-1"
|
||||
assert search_opts["flavor"] == "flavor-1"
|
||||
assert search_opts["uuid"] == "uuid-1"
|
||||
assert search_opts["ip"] == "10.0.0.5"
|
||||
|
18
swagger.json
18
swagger.json
@@ -522,6 +522,24 @@
|
||||
},
|
||||
"description": "Filter the list of servers by the given server UUID."
|
||||
},
|
||||
{
|
||||
"name": "ip",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"description": "Filter the list of servers by the given IP address (only fixed, not floating). Also passed to Nova API if supported.",
|
||||
"title": "Ip"
|
||||
},
|
||||
"description": "Filter the list of servers by the given IP address (only fixed, not floating). Also passed to Nova API if supported."
|
||||
},
|
||||
{
|
||||
"name": "X-Openstack-Request-Id",
|
||||
"in": "header",
|
||||
|
Reference in New Issue
Block a user