diff --git a/src/openec2/actions/describe_instances.py b/src/openec2/actions/describe_instances.py index 0684230..be2bf4f 100644 --- a/src/openec2/actions/describe_instances.py +++ b/src/openec2/actions/describe_instances.py @@ -5,7 +5,7 @@ from fastapi.datastructures import QueryParams from sqlmodel import select 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.config import OpenEC2Config from openec2.db import DatabaseDep @@ -30,7 +30,14 @@ def describe_instances( DescribeInstancesResponse( request_id=uuid.uuid4().hex, reservation_set=DescribeInstancesResponseReservationSet( - item=response_items, + item=[ + ReservationSet( + reservationId=instance.instance_id, + instancesSet=ReservationSetInstancesSet( + item=[instance], + ), + ) for instance in response_items + ], ), ).to_xml(), media_type="application/xml", diff --git a/src/openec2/actions/run_instances.py b/src/openec2/actions/run_instances.py index 09166ce..6838a5f 100644 --- a/src/openec2/actions/run_instances.py +++ b/src/openec2/actions/run_instances.py @@ -2,7 +2,7 @@ import base64 from typing import cast import uuid -from fastapi import HTTPException +from fastapi import HTTPException, Response from fastapi.datastructures import QueryParams from sqlmodel import select @@ -161,9 +161,12 @@ def run_instances( description = describe_instance(instance, domain) db.commit() - return RunInstanceResponse( - request_id=uuid.uuid4().hex, - instance_set=RunInstanceInstanceSet( - item=[description], - ), - ).to_xml() + return Response( + RunInstanceResponse( + request_id=uuid.uuid4().hex, + instance_set=RunInstanceInstanceSet( + item=[description], + ), + ).to_xml(), + media_type="application/xml", + ) diff --git a/src/openec2/actions/start_instances.py b/src/openec2/actions/start_instances.py index ca6a39f..724fcf7 100644 --- a/src/openec2/actions/start_instances.py +++ b/src/openec2/actions/start_instances.py @@ -1,6 +1,6 @@ import uuid -from fastapi import HTTPException +from fastapi import HTTPException, Response from fastapi.datastructures import QueryParams from sqlmodel import select @@ -9,7 +9,9 @@ from openec2.config import OpenEC2Config from openec2.db import DatabaseDep from openec2.db.instance import Instance 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( @@ -18,7 +20,7 @@ def start_instances( db: DatabaseDep, ): conn = LibvirtSingleton.of().connection - instances: list[StartInstancesResponseInstancesSetInstance] = [] + instances: list[InstanceInfo] = [] for instance_id in parse_array_plain("InstanceId", params): instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() if instance is None: @@ -26,30 +28,25 @@ def start_instances( dom = conn.lookupByName(instance_id) - running = dom.isActive() - prev_state = InstanceState( - code=16 if running else 80, - name="running" if running else "stopped", - ) + prev_state = describe_instance_state(dom) if not dom.isActive(): dom.create() - running = dom.isActive() - current_state = InstanceState( - code=16 if running else 80, - name="running" if running else "stopped", - ) + current_state = describe_instance_state(dom) instances.append( - StartInstancesResponseInstancesSetInstance( - instance_id=instance_id, - previous_state=prev_state, - current_state=current_state, + InstanceInfo( + instanceId=instance_id, + previousState=prev_state, + currentState=current_state, ), ) - return StartInstancesResponse( - request_id=uuid.uuid4().hex, - instances_set=StartInstancesResponseInstancesSet( - item=instances, - ), - ).to_xml() + return Response( + StartInstancesResponse( + request_id=uuid.uuid4().hex, + instancesSet=InstancesSet( + item=instances, + ), + ).to_xml(), + media_type="application/xml", + ) diff --git a/src/openec2/actions/stop_instances.py b/src/openec2/actions/stop_instances.py index 61e4f38..0d3d83a 100644 --- a/src/openec2/actions/stop_instances.py +++ b/src/openec2/actions/stop_instances.py @@ -1,6 +1,6 @@ import uuid -from fastapi import HTTPException +from fastapi import HTTPException, Response from fastapi.datastructures import QueryParams from sqlmodel import select @@ -9,8 +9,9 @@ from openec2.config import OpenEC2Config from openec2.db import DatabaseDep from openec2.db.instance import Instance from openec2.utils.array import parse_array_plain -from openec2.api.shared import InstanceState -from openec2.api.stop_instances import StopInstancesResponse, StopInstancesResponseInstancesSet, StopInstancesResponseInstancesSetInstance +from openec2.api.shared import InstanceInfo, InstancesSet +from openec2.api.describe_instances import describe_instance_state +from openec2.api.stop_instances import StopInstancesResponse def stop_instances( @@ -19,7 +20,7 @@ def stop_instances( db: DatabaseDep, ): conn = LibvirtSingleton.of().connection - instances: list[StopInstancesResponseInstancesSetInstance] = [] + instances: list[InstanceInfo] = [] for instance_id in parse_array_plain("InstanceId", params): instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() if instance is None: @@ -27,29 +28,26 @@ def stop_instances( dom = conn.lookupByName(instance_id) running = dom.isActive() - prev_state = InstanceState( - code=16 if running else 80, - name="running" if running else "stopped", - ) + prev_state = describe_instance_state(dom) if running: dom.shutdown() running = dom.isActive() - current_state = InstanceState( - code=16 if running else 80, - name="running" if running else "stopped", - ) + current_state = describe_instance_state(dom) instances.append( - StopInstancesResponseInstancesSetInstance( - instance_id=instance_id, - previous_state=prev_state, - current_state=current_state, + InstanceInfo( + instanceId=instance_id, + previousState=prev_state, + currentState=current_state, ), ) - return StopInstancesResponse( - request_id=uuid.uuid4().hex, - instances_set=StopInstancesResponseInstancesSet( - item=instances, - ), - ).to_xml() + return Response( + StopInstancesResponse( + requestId=uuid.uuid4().hex, + instancesSet=InstancesSet( + item=instances, + ), + ).to_xml(), + media_type="application/xml", + ) diff --git a/src/openec2/actions/terminate_instances.py b/src/openec2/actions/terminate_instances.py index 5b07371..042e179 100644 --- a/src/openec2/actions/terminate_instances.py +++ b/src/openec2/actions/terminate_instances.py @@ -1,6 +1,7 @@ import logging +import uuid -from fastapi import HTTPException +from fastapi import HTTPException, Response from fastapi.datastructures import QueryParams from sqlmodel import select @@ -11,6 +12,8 @@ from openec2.db.instance import Instance from openec2.utils.array import parse_array_plain from openec2.images import garbage_collect_image 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() @@ -20,18 +23,31 @@ def terminate_instances( config: OpenEC2Config, db: DatabaseDep, ): + instances: list[InstanceInfo] = [] + conn = LibvirtSingleton.of().connection image_ids: set[str] = set() for instance_id in parse_array_plain("InstanceId", params): instance = db.exec(select(Instance).where(Instance.id == instance_id)).first() 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) + prev_state = describe_instance_state(dom) if dom.isActive(): dom.shutdown() + current_state = describe_instance_state(dom) dom.undefine() + instances.append( + InstanceInfo( + instanceId=instance.id, + currentState=current_state, + previousState=prev_state, + ), + ) + # Delete files logger.debug(f"Removing {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: 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", + ) diff --git a/src/openec2/api/describe_instances.py b/src/openec2/api/describe_instances.py index f936c5f..e8738e8 100644 --- a/src/openec2/api/describe_instances.py +++ b/src/openec2/api/describe_instances.py @@ -21,11 +21,19 @@ class InstanceDescription( instance_state: InstanceState = element(tag="instanceState") tag_set: InstanceTagSet = element(tag="tagSet") + +class ReservationSetInstancesSet(BaseXmlModel): + item: list[InstanceDescription] = element() + +class ReservationSet(BaseXmlModel): + reservationId: str = element() + instancesSet: ReservationSetInstancesSet = element() + class DescribeInstancesResponseReservationSet( BaseXmlModel, tag="reservationSet", ): - item: list[InstanceDescription] = element() + item: list[ReservationSet] = element("") class DescribeInstancesResponse( BaseXmlModel, @@ -36,15 +44,18 @@ class DescribeInstancesResponse( 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() + 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( instance_id=instance.id, image_id=instance.imageId, - instance_state=InstanceState( - code=16 if running else 80, - name="running" if running else "stopped", - ), + instance_state=describe_instance_state(domain), tag_set=InstanceTagSet( item=[ InstanceTag( diff --git a/src/openec2/api/shared.py b/src/openec2/api/shared.py index 214c121..9782c01 100644 --- a/src/openec2/api/shared.py +++ b/src/openec2/api/shared.py @@ -4,3 +4,13 @@ from pydantic_xml import BaseXmlModel, element class InstanceState(BaseXmlModel): code: int = 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() diff --git a/src/openec2/api/start_instances.py b/src/openec2/api/start_instances.py index f7899c6..b199548 100644 --- a/src/openec2/api/start_instances.py +++ b/src/openec2/api/start_instances.py @@ -1,26 +1,12 @@ 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( BaseXmlModel, tag="StartInstancesResponse", nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"} ): request_id: str = element(tag="requestId") - instances_set: StartInstancesResponseInstancesSet + instancesSet: InstancesSet = element() diff --git a/src/openec2/api/stop_instances.py b/src/openec2/api/stop_instances.py index ca88fe7..932b150 100644 --- a/src/openec2/api/stop_instances.py +++ b/src/openec2/api/stop_instances.py @@ -1,26 +1,12 @@ 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( BaseXmlModel, tag="StopInstancesResponse", nsmap={"": "http://ec2.amazonaws.com/doc/2016-11-15/"} ): - request_id: str = element(tag="requestId") - instances_set: StopInstancesResponseInstancesSet + requestId: str = element(tag="requestId") + instancesSet: InstancesSet = element() diff --git a/src/openec2/api/terminate_instances.py b/src/openec2/api/terminate_instances.py new file mode 100644 index 0000000..851527c --- /dev/null +++ b/src/openec2/api/terminate_instances.py @@ -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()