This commit is contained in:
PapaTutuWawa 2025-03-29 15:47:29 +01:00
parent 5b99e6e990
commit e92994d2f3
9 changed files with 60 additions and 38 deletions

View File

@ -3,8 +3,8 @@ import uuid
from fastapi import Response
from fastapi.datastructures import QueryParams
from sqlmodel import select
import libvirt
from openec2.libvirt import LibvirtSingleton
from openec2.api.describe_instances import InstanceDescription, DescribeInstancesResponse, DescribeInstancesResponseReservationSet, describe_instance
from openec2.api.shared import InstanceState
from openec2.config import OpenEC2Config
@ -17,7 +17,7 @@ def describe_instances(
db: DatabaseDep,
):
response_items: list[InstanceDescription] = []
conn = libvirt.open("qemu:///system")
conn = LibvirtSingleton.of().connection
for instance in db.exec(select(Instance)).all():
dom = conn.lookupByName(instance.id)
running = dom.isActive()
@ -25,7 +25,6 @@ def describe_instances(
response_items.append(
describe_instance(instance, dom),
)
conn.close()
return Response(
DescribeInstancesResponse(

View File

@ -6,8 +6,8 @@ import os
from fastapi.datastructures import QueryParams
from sqlmodel import select
import libvirt
from openec2.libvirt import LibvirtSingleton
from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep
from openec2.db.instance import Instance
@ -15,7 +15,6 @@ from openec2.db.image import AMI
from openec2.api.run_instances import RunInstanceResponse, RunInstanceInstanceSet
from openec2.api.describe_instances import describe_instance
from openec2.utils.array import parse_array_objects
from openec2.utils.cloudinit import create_cloudinit_image
def create_libvirt_domain(
name: str,
@ -121,7 +120,7 @@ def run_instances(
db.commit()
print("Inserted new instance")
conn = libvirt.open("qemu:///system")
conn = LibvirtSingleton.of().connection
domain = conn.defineXML(
create_libvirt_domain(
instance_id,
@ -133,7 +132,6 @@ def run_instances(
)
domain.create()
description = describe_instance(instance, domain)
conn.close()
return RunInstanceResponse(
request_id=uuid.uuid4().hex,

View File

@ -3,8 +3,8 @@ import uuid
from fastapi import HTTPException
from fastapi.datastructures import QueryParams
from sqlmodel import select
import libvirt
from openec2.libvirt import LibvirtSingleton
from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep
from openec2.db.instance import Instance
@ -17,13 +17,13 @@ def start_instances(
config: OpenEC2Config,
db: DatabaseDep,
):
conn = LibvirtSingleton.of().connection
instances: list[StartInstancesResponseInstancesSetInstance] = []
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")
conn = libvirt.open("qemu:///system")
dom = conn.lookupByName(instance_id)
running = dom.isActive()
@ -38,7 +38,6 @@ def start_instances(
code=16 if running else 80,
name="running" if running else "stopped",
)
conn.close()
instances.append(
StartInstancesResponseInstancesSetInstance(

View File

@ -3,8 +3,8 @@ import uuid
from fastapi import HTTPException
from fastapi.datastructures import QueryParams
from sqlmodel import select
import libvirt
from openec2.libvirt import LibvirtSingleton
from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep
from openec2.db.instance import Instance
@ -18,13 +18,13 @@ def stop_instances(
config: OpenEC2Config,
db: DatabaseDep,
):
conn = LibvirtSingleton.of().connection
instances: list[StopInstancesResponseInstancesSetInstance] = []
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")
conn = libvirt.open("qemu:///system")
dom = conn.lookupByName(instance_id)
running = dom.isActive()
prev_state = InstanceState(
@ -38,7 +38,6 @@ def stop_instances(
code=16 if running else 80,
name="running" if running else "stopped",
)
conn.close()
instances.append(
StopInstancesResponseInstancesSetInstance(

View File

@ -3,8 +3,8 @@ import logging
from fastapi import HTTPException
from fastapi.datastructures import QueryParams
from sqlmodel import select
import libvirt
from openec2.libvirt import LibvirtSingleton
from openec2.config import OpenEC2Config
from openec2.db import DatabaseDep
from openec2.db.instance import Instance
@ -18,17 +18,16 @@ def terminate_instances(
config: OpenEC2Config,
db: DatabaseDep,
):
conn = LibvirtSingleton.of().connection
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")
conn = libvirt.open("qemu:///system")
dom = conn.lookupByName(instance_id)
if dom.isActive():
dom.shutdown()
dom.undefine()
conn.close()
# Delete files
logger.debug(f"Removing {config.instances.location / instance_id}")

View File

@ -1,5 +1,4 @@
from pydantic_xml import BaseXmlModel, element
import libvirt
from openec2.db.instance import Instance
from openec2.api.shared import InstanceState

View File

@ -13,12 +13,16 @@ class _OpenEC2InstanceConfig(BaseModel):
types: dict[str, _OpenEC2InstanceType]
class _OpenEC2LibvirtConfig(BaseModel):
connection: str
class _OpenEC2Config(BaseModel):
images: Path
seed: Path
instances: _OpenEC2InstanceConfig
libvirt: _OpenEC2LibvirtConfig
def get_config() -> _OpenEC2Config:
def _get_config() -> _OpenEC2Config:
# TODO: Read from disk
return _OpenEC2Config(
images=Path("/home/alexander/openec2/images"),
@ -33,6 +37,26 @@ def get_config() -> _OpenEC2Config:
),
},
),
libvirt=_OpenEC2LibvirtConfig(
connection="qemu:///system"
),
)
OpenEC2Config = Annotated[_OpenEC2Config, Depends(get_config)]
class ConfigSingleton:
__instance: "ConfigSingleton | None" = None
config: _OpenEC2Config
def __init__(self):
self.config = _get_config()
def get_config(self) -> _OpenEC2Config:
return self.config
@staticmethod
def of() -> "ConfigSingleton":
if ConfigSingleton.__instance is None:
ConfigSingleton.__instance = ConfigSingleton()
return ConfigSingleton.__instance
OpenEC2Config = Annotated[_OpenEC2Config, Depends(ConfigSingleton.of().get_config)]

24
src/openec2/libvirt.py Normal file
View File

@ -0,0 +1,24 @@
import libvirt
from openec2.config import ConfigSingleton
class LibvirtSingleton:
__instance: "LibvirtSingleton | None" = None
# The connection to libvirt
connection: libvirt.virConnect
def __init__(self):
self.connection = libvirt.open(
ConfigSingleton.of().config.libvirt.connection,
)
def __del__(self):
self.connection.close()
@staticmethod
def of() -> "LibvirtSingleton":
if LibvirtSingleton.__instance is None:
LibvirtSingleton.__instance = LibvirtSingleton()
return LibvirtSingleton.__instance

View File

@ -1,19 +0,0 @@
import os
from pathlib import Path
import tempfile
def create_cloudinit_image(
seed_file: Path,
instance_id: str,
user_data: str,
):
with tempfile.TemporaryDirectory() as _tmp:
tmp = Path(_tmp)
with (tmp / "meta-data").open("w") as f:
f.write(f"instance-id: {instance_id}\nlocal-hostname: {instance_id}")
with (tmp / "user-data").open("w") as f:
f.write(user_data)
os.system(f"truncate --size 2M {seed_file}")
os.system(f"mkfs.vfat -n cidata {seed_file}")
os.system(f"mcopy -oi {seed_file} {tmp / "meta-data"} {tmp / "user-data"} ::")