Fix XML responses

This commit is contained in:
PapaTutuWawa 2025-03-30 22:57:54 +02:00
parent 6b4eaa6448
commit 97f3a12617
10 changed files with 131 additions and 96 deletions

View File

@ -5,7 +5,7 @@ from fastapi.datastructures import QueryParams
from sqlmodel import select from sqlmodel import select
from openec2.libvirt import LibvirtSingleton from openec2.libvirt import LibvirtSingleton
from openec2.api.describe_instances import InstanceDescription, DescribeInstancesResponse, DescribeInstancesResponseReservationSet, describe_instance from openec2.api.describe_instances import InstanceDescription, DescribeInstancesResponse, DescribeInstancesResponseReservationSet, ReservationSet, ReservationSetInstancesSet, describe_instance
from openec2.api.shared import InstanceState from openec2.api.shared import InstanceState
from openec2.config import OpenEC2Config from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep from openec2.db import DatabaseDep
@ -30,7 +30,14 @@ def describe_instances(
DescribeInstancesResponse( DescribeInstancesResponse(
request_id=uuid.uuid4().hex, request_id=uuid.uuid4().hex,
reservation_set=DescribeInstancesResponseReservationSet( reservation_set=DescribeInstancesResponseReservationSet(
item=response_items, item=[
ReservationSet(
reservationId=instance.instance_id,
instancesSet=ReservationSetInstancesSet(
item=[instance],
),
) for instance in response_items
],
), ),
).to_xml(), ).to_xml(),
media_type="application/xml", media_type="application/xml",

View File

@ -2,7 +2,7 @@ import base64
from typing import cast from typing import cast
import uuid import uuid
from fastapi import HTTPException from fastapi import HTTPException, Response
from fastapi.datastructures import QueryParams from fastapi.datastructures import QueryParams
from sqlmodel import select from sqlmodel import select
@ -161,9 +161,12 @@ def run_instances(
description = describe_instance(instance, domain) description = describe_instance(instance, domain)
db.commit() db.commit()
return RunInstanceResponse( return Response(
RunInstanceResponse(
request_id=uuid.uuid4().hex, request_id=uuid.uuid4().hex,
instance_set=RunInstanceInstanceSet( instance_set=RunInstanceInstanceSet(
item=[description], item=[description],
), ),
).to_xml() ).to_xml(),
media_type="application/xml",
)

View File

@ -1,6 +1,6 @@
import uuid import uuid
from fastapi import HTTPException from fastapi import HTTPException, Response
from fastapi.datastructures import QueryParams from fastapi.datastructures import QueryParams
from sqlmodel import select from sqlmodel import select
@ -9,7 +9,9 @@ from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep from openec2.db import DatabaseDep
from openec2.db.instance import Instance from openec2.db.instance import Instance
from openec2.utils.array import parse_array_plain from openec2.utils.array import parse_array_plain
from openec2.api.start_instances import InstanceState, StartInstancesResponseInstancesSetInstance, StartInstancesResponse, StartInstancesResponseInstancesSet from openec2.api.shared import InstanceInfo, InstancesSet
from openec2.api.describe_instances import describe_instance_state
from openec2.api.start_instances import StartInstancesResponse
def start_instances( def start_instances(
@ -18,7 +20,7 @@ def start_instances(
db: DatabaseDep, db: DatabaseDep,
): ):
conn = LibvirtSingleton.of().connection conn = LibvirtSingleton.of().connection
instances: list[StartInstancesResponseInstancesSetInstance] = [] instances: list[InstanceInfo] = []
for instance_id in parse_array_plain("InstanceId", params): for instance_id in parse_array_plain("InstanceId", params):
instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() instance = db.exec(select(Instance).where(Instance.id == instance_id)).first()
if instance is None: if instance is None:
@ -26,30 +28,25 @@ def start_instances(
dom = conn.lookupByName(instance_id) dom = conn.lookupByName(instance_id)
running = dom.isActive() prev_state = describe_instance_state(dom)
prev_state = InstanceState(
code=16 if running else 80,
name="running" if running else "stopped",
)
if not dom.isActive(): if not dom.isActive():
dom.create() dom.create()
running = dom.isActive() current_state = describe_instance_state(dom)
current_state = InstanceState(
code=16 if running else 80,
name="running" if running else "stopped",
)
instances.append( instances.append(
StartInstancesResponseInstancesSetInstance( InstanceInfo(
instance_id=instance_id, instanceId=instance_id,
previous_state=prev_state, previousState=prev_state,
current_state=current_state, currentState=current_state,
), ),
) )
return StartInstancesResponse( return Response(
StartInstancesResponse(
request_id=uuid.uuid4().hex, request_id=uuid.uuid4().hex,
instances_set=StartInstancesResponseInstancesSet( instancesSet=InstancesSet(
item=instances, item=instances,
), ),
).to_xml() ).to_xml(),
media_type="application/xml",
)

View File

@ -1,6 +1,6 @@
import uuid import uuid
from fastapi import HTTPException from fastapi import HTTPException, Response
from fastapi.datastructures import QueryParams from fastapi.datastructures import QueryParams
from sqlmodel import select from sqlmodel import select
@ -9,8 +9,9 @@ from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep from openec2.db import DatabaseDep
from openec2.db.instance import Instance from openec2.db.instance import Instance
from openec2.utils.array import parse_array_plain from openec2.utils.array import parse_array_plain
from openec2.api.shared import InstanceState from openec2.api.shared import InstanceInfo, InstancesSet
from openec2.api.stop_instances import StopInstancesResponse, StopInstancesResponseInstancesSet, StopInstancesResponseInstancesSetInstance from openec2.api.describe_instances import describe_instance_state
from openec2.api.stop_instances import StopInstancesResponse
def stop_instances( def stop_instances(
@ -19,7 +20,7 @@ def stop_instances(
db: DatabaseDep, db: DatabaseDep,
): ):
conn = LibvirtSingleton.of().connection conn = LibvirtSingleton.of().connection
instances: list[StopInstancesResponseInstancesSetInstance] = [] instances: list[InstanceInfo] = []
for instance_id in parse_array_plain("InstanceId", params): for instance_id in parse_array_plain("InstanceId", params):
instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() instance = db.exec(select(Instance).where(Instance.id == instance_id)).first()
if instance is None: if instance is None:
@ -27,29 +28,26 @@ def stop_instances(
dom = conn.lookupByName(instance_id) dom = conn.lookupByName(instance_id)
running = dom.isActive() running = dom.isActive()
prev_state = InstanceState( prev_state = describe_instance_state(dom)
code=16 if running else 80,
name="running" if running else "stopped",
)
if running: if running:
dom.shutdown() dom.shutdown()
running = dom.isActive() running = dom.isActive()
current_state = InstanceState( current_state = describe_instance_state(dom)
code=16 if running else 80,
name="running" if running else "stopped",
)
instances.append( instances.append(
StopInstancesResponseInstancesSetInstance( InstanceInfo(
instance_id=instance_id, instanceId=instance_id,
previous_state=prev_state, previousState=prev_state,
current_state=current_state, currentState=current_state,
), ),
) )
return StopInstancesResponse( return Response(
request_id=uuid.uuid4().hex, StopInstancesResponse(
instances_set=StopInstancesResponseInstancesSet( requestId=uuid.uuid4().hex,
instancesSet=InstancesSet(
item=instances, item=instances,
), ),
).to_xml() ).to_xml(),
media_type="application/xml",
)

View File

@ -1,6 +1,7 @@
import logging import logging
import uuid
from fastapi import HTTPException from fastapi import HTTPException, Response
from fastapi.datastructures import QueryParams from fastapi.datastructures import QueryParams
from sqlmodel import select from sqlmodel import select
@ -11,6 +12,8 @@ from openec2.db.instance import Instance
from openec2.utils.array import parse_array_plain from openec2.utils.array import parse_array_plain
from openec2.images import garbage_collect_image from openec2.images import garbage_collect_image
from openec2.ipam import remove_instance_dhcp_mapping from openec2.ipam import remove_instance_dhcp_mapping
from openec2.api.describe_instances import describe_instance_state
from openec2.api.terminate_instances import TerminateInstancesResponse, InstancesSet
logger = logging.getLogger() logger = logging.getLogger()
@ -20,18 +23,31 @@ def terminate_instances(
config: OpenEC2Config, config: OpenEC2Config,
db: DatabaseDep, db: DatabaseDep,
): ):
instances: list[InstanceInfo] = []
conn = LibvirtSingleton.of().connection conn = LibvirtSingleton.of().connection
image_ids: set[str] = set() image_ids: set[str] = set()
for instance_id in parse_array_plain("InstanceId", params): for instance_id in parse_array_plain("InstanceId", params):
instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() instance = db.exec(select(Instance).where(Instance.id == instance_id)).first()
if instance is None: if instance is None:
raise HTTPException(status_code=404, detail="Unknown instance") continue
#raise HTTPException(status_code=404, detail="Unknown instance")
dom = conn.lookupByName(instance_id) dom = conn.lookupByName(instance_id)
prev_state = describe_instance_state(dom)
if dom.isActive(): if dom.isActive():
dom.shutdown() dom.shutdown()
current_state = describe_instance_state(dom)
dom.undefine() dom.undefine()
instances.append(
InstanceInfo(
instanceId=instance.id,
currentState=current_state,
previousState=prev_state,
),
)
# Delete files # Delete files
logger.debug(f"Removing {config.instances.location / instance_id}") logger.debug(f"Removing {config.instances.location / instance_id}")
instance_disk = config.instances.location / instance_id instance_disk = config.instances.location / instance_id
@ -47,4 +63,12 @@ def terminate_instances(
for image_id in image_ids: for image_id in image_ids:
garbage_collect_image(image_id, db) garbage_collect_image(image_id, db)
return "OK" return Response(
TerminateInstancesResponse(
requestId=uuid.uuid4().hex,
instancesSet=InstancesSet(
item=instances,
),
).to_xml(),
media_type="application/xml",
)

View File

@ -21,11 +21,19 @@ class InstanceDescription(
instance_state: InstanceState = element(tag="instanceState") instance_state: InstanceState = element(tag="instanceState")
tag_set: InstanceTagSet = element(tag="tagSet") tag_set: InstanceTagSet = element(tag="tagSet")
class ReservationSetInstancesSet(BaseXmlModel):
item: list[InstanceDescription] = element()
class ReservationSet(BaseXmlModel):
reservationId: str = element()
instancesSet: ReservationSetInstancesSet = element()
class DescribeInstancesResponseReservationSet( class DescribeInstancesResponseReservationSet(
BaseXmlModel, BaseXmlModel,
tag="reservationSet", tag="reservationSet",
): ):
item: list[InstanceDescription] = element() item: list[ReservationSet] = element("")
class DescribeInstancesResponse( class DescribeInstancesResponse(
BaseXmlModel, BaseXmlModel,
@ -36,15 +44,18 @@ class DescribeInstancesResponse(
reservation_set: DescribeInstancesResponseReservationSet = element("reservationSet") reservation_set: DescribeInstancesResponseReservationSet = element("reservationSet")
def describe_instance(instance: Instance, domain: libvirt.virDomain) -> InstanceDescription: def describe_instance_state(domain: libvirt.virDomain) -> InstanceState:
running = domain.isActive() running = domain.isActive()
return InstanceState(
code=16 if running else 80,
name="running" if running else "stopped",
)
def describe_instance(instance: Instance, domain: libvirt.virDomain) -> InstanceDescription:
return InstanceDescription( return InstanceDescription(
instance_id=instance.id, instance_id=instance.id,
image_id=instance.imageId, image_id=instance.imageId,
instance_state=InstanceState( instance_state=describe_instance_state(domain),
code=16 if running else 80,
name="running" if running else "stopped",
),
tag_set=InstanceTagSet( tag_set=InstanceTagSet(
item=[ item=[
InstanceTag( InstanceTag(

View File

@ -4,3 +4,13 @@ from pydantic_xml import BaseXmlModel, element
class InstanceState(BaseXmlModel): class InstanceState(BaseXmlModel):
code: int = element() code: int = element()
name: str = element() name: str = element()
class InstanceInfo(BaseXmlModel):
instanceId: str = element()
currentState: InstanceState = element()
previousState: InstanceState = element()
class InstancesSet(BaseXmlModel, tag="instancesSet"):
item: list[InstanceInfo] = element()

View File

@ -1,26 +1,12 @@
from pydantic_xml import BaseXmlModel, element from pydantic_xml import BaseXmlModel, element
from openec2.api.shared import InstanceState from openec2.api.shared import InstancesSet
class StartInstancesResponseInstancesSetInstance(
BaseXmlModel,
tag="item",
):
instance_id: str = element(tag="instanceId")
previous_state: InstanceState = element("previousState")
current_state: InstanceState = element("currentState")
class StartInstancesResponseInstancesSet(
BaseXmlModel,
tag="instancesSet",
):
item: list[StartInstancesResponseInstancesSetInstance] = element()
class StartInstancesResponse( class StartInstancesResponse(
BaseXmlModel, BaseXmlModel,
tag="StartInstancesResponse", tag="StartInstancesResponse",
nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"} nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"}
): ):
request_id: str = element(tag="requestId") request_id: str = element(tag="requestId")
instances_set: StartInstancesResponseInstancesSet instancesSet: InstancesSet = element()

View File

@ -1,26 +1,12 @@
from pydantic_xml import BaseXmlModel, element from pydantic_xml import BaseXmlModel, element
from openec2.api.shared import InstanceState from openec2.api.shared import InstancesSet
class StopInstancesResponseInstancesSetInstance(
BaseXmlModel,
tag="item",
):
instance_id: str = element(tag="instanceId")
previous_state: InstanceState = element("previousState")
current_state: InstanceState = element("currentState")
class StopInstancesResponseInstancesSet(
BaseXmlModel,
tag="instancesSet",
):
item: list[StopInstancesResponseInstancesSetInstance] = element()
class StopInstancesResponse( class StopInstancesResponse(
BaseXmlModel, BaseXmlModel,
tag="StopInstancesResponse", tag="StopInstancesResponse",
nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"} nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"}
): ):
request_id: str = element(tag="requestId") requestId: str = element(tag="requestId")
instances_set: StopInstancesResponseInstancesSet instancesSet: InstancesSet = element()

View File

@ -0,0 +1,13 @@
from pydantic_xml import BaseXmlModel, element
from openec2.api.shared import InstancesSet
class TerminateInstancesResponse(
BaseXmlModel,
tag="TerminateInstancesResponse",
nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"},
):
requestId: str = element()
instancesSet: InstancesSet = element()