Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ provisioners:
port: 6379
db: 0

services:
service-definitions:
- name: qwen1.5-vllm
type: container
description: DeepSeek Qwen 1.5B via vLLM
Expand Down Expand Up @@ -170,8 +170,8 @@ Configuration reference
- `hosts[]`: Named machines and their IPs.
- `provisioners[]`: Defines where the provisioner runs and its state cache
(Redis).
- `services[]`: Descriptions of services, including hardware `varieties` and
runtime `profiles`.
- `service-definitions[]`: Descriptions of services, including hardware
`varieties` and runtime `profiles`.

CLI usage
---------
Expand Down
2 changes: 1 addition & 1 deletion dev/resources/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ provisioners:
port: 6479
db: 0

services:
service-definitions:
- name: simple_test_1
type: container
description: Simple test service
Expand Down
2 changes: 1 addition & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""the orchestration module coordinates the models and context classes into
one or more services.
one or more service_definitions.
"""
6 changes: 3 additions & 3 deletions src/api/provisioner.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""FastAPI application for the Ozwald Provisioner service.

This API allows an orchestrator to control which services are provisioned
and provides information about available resources.
This API allows an orchestrator to control which service_definitions
are provisioned and provides information about available resources.
"""

from __future__ import annotations
Expand Down Expand Up @@ -349,7 +349,7 @@ async def _get_service_runner_logs(
provisioner = SystemProvisioner.singleton()
cache = provisioner.get_cache()

# Try to resolve container name from active services
# Try to resolve container name from active service_definitions
active_cache = ActiveServicesCache(cache)
active_services = active_cache.get_services()

Expand Down
26 changes: 17 additions & 9 deletions src/command/ozwald.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,9 @@ def _parse_services_spec(spec: str) -> list[dict[str, Any]]:
for raw in [p.strip() for p in (spec or "").split(",") if p.strip()]:
result.append(_parse_services_spec_entry(raw, cfg))
if not result:
raise ValueError("No services parsed from specification string")
raise ValueError(
"No service_definitions parsed from specification string"
)
return result


Expand Down Expand Up @@ -466,7 +468,9 @@ def _parse_footprint_spec(spec: str) -> list[dict[str, Any]]:
for raw in [p.strip() for p in (spec or "").split(",") if p.strip()]:
result.append(_parse_footprint_spec_entry(raw, cfg))
if not result:
raise ValueError("No services parsed from footprint specification")
raise ValueError(
"No service_definitions parsed from footprint specification"
)
return result


Expand All @@ -483,12 +487,13 @@ def action_footprint_services(
if all_services:
if spec:
print(
"Warning: services specification ignored when using --all"
"Warning: service_definitions specification ignored when "
"using --all"
)
else:
if not spec:
print(
"Error: services specification is required when "
"Error: service_definitions specification is required when "
"not using --all",
)
return 2
Expand All @@ -505,7 +510,9 @@ def action_footprint_services(
print(f"Unexpected response: {json.dumps(data)}")
return 2
except Exception as e:
print(f"Error footprinting services: {type(e).__name__}({e})")
print(
f"Error footprinting service_definitions: {type(e).__name__}({e})"
)
return 2


Expand All @@ -516,7 +523,7 @@ def action_update_services(port: int, clear: bool, spec: str | None) -> int:
else:
if not spec:
print(
"Error: services specification is required when "
"Error: service_definitions specification is required when "
"not using --clear",
)
return 2
Expand All @@ -534,7 +541,7 @@ def action_update_services(port: int, clear: bool, spec: str | None) -> int:
print(f"Unexpected response: {json.dumps(data)}")
return 2
except Exception as e:
print(f"Error updating services: {type(e).__name__}({e})")
print(f"Error updating service_definitions: {type(e).__name__}({e})")
return 2


Expand Down Expand Up @@ -696,13 +703,14 @@ def build_parser() -> argparse.ArgumentParser:
"--clear",
action="store_true",
help=(
"For update_services: send an empty list to clear active services"
"For update_services: send an empty list to clear active "
"service_definitions"
),
)
parser.add_argument(
"--all",
action="store_true",
help="For footprint_services: footprint all services",
help="For footprint_services: footprint all service_definitions",
)
parser.add_argument(
"--profile",
Expand Down
15 changes: 8 additions & 7 deletions src/config/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self, config_path: str):

# Initialize attributes that will be populated
self.hosts: List[Host] = []
self.services: List[ServiceDefinition] = []
self.service_definitions: List[ServiceDefinition] = []
self.provisioners: List[Provisioner] = []
self.networks: List[Network] = []
# Top-level named volumes (normalized)
Expand Down Expand Up @@ -69,7 +69,7 @@ def _parse_config(self) -> None:
self._parse_hosts()
self._parse_networks()
self._parse_volumes()
self._parse_services()
self._parse_service_definitions()
self._parse_provisioners()

# ---------------- Internal helpers -----------------
Expand Down Expand Up @@ -202,15 +202,16 @@ def _parse_networks(self) -> None:
network = Network(name=network_data["name"])
self.networks.append(network)

def _parse_services(self) -> None:
"""Parse services section and create ServiceDefinition models.
def _parse_service_definitions(self) -> None:
"""
Parse service-definitions section and create ServiceDefinition models.

Supports service-level profiles and varieties. Varieties behave like
alternative definitions (e.g., different container images) that can
override docker-compose-like fields; parent-level fields are used as
defaults and merged appropriately.
"""
services_data = self._raw_config.get("services", [])
services_data = self._raw_config.get("service-definitions", [])

for i, service_data in enumerate(services_data):
if "name" not in service_data:
Expand Down Expand Up @@ -346,7 +347,7 @@ def _parse_services(self) -> None:
profiles=profiles_dict,
varieties=varieties,
)
self.services.append(service_def)
self.service_definitions.append(service_def)

def _normalize_service_volumes(self, raw_vols) -> List[str]:
"""Return a list of docker-ready volume strings.
Expand Down Expand Up @@ -487,7 +488,7 @@ def get_service_by_name(
) -> Optional[ServiceDefinition]:
"""Get a service definition by service_name."""
result = None
for service in self.services:
for service in self.service_definitions:
if service.service_name == service_name:
result = service
# logger.info("get_service_by_name %s -> %s", service_name, result)
Expand Down
15 changes: 9 additions & 6 deletions src/orchestration/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ class ServiceDefinition(BaseModel):
type: str | ServiceType
description: str | None = None

# image is required for CONTAINER services, but optional for SOURCE_FILES
# and API services.
# image is required for CONTAINER service_definitions, but optional for
# SOURCE_FILES and API service_definitions.
image: str | None = ""

depends_on: list[str] | None = Field(default_factory=list)
Expand Down Expand Up @@ -266,7 +266,10 @@ class ProvisionerState(BaseModel):

class OzwaldConfig(BaseModel):
hosts: list[Host] = Field(default_factory=list)
services: list[ServiceDefinition] = Field(default_factory=list)
service_definitions: list[ServiceDefinition] = Field(
default_factory=list,
alias="service-definitions",
)
provisioners: list[Provisioner] = Field(default_factory=list)
networks: list[Network] = Field(default_factory=list)
# Top-level named volume specifications (parsed/normalized by reader)
Expand All @@ -289,7 +292,7 @@ class ProvisionerProfile(BaseModel):


class ResourceConstraints(BaseModel):
"""Resource requirements and constraints for services"""
"""Resource requirements and constraints for service_definitions"""

gpu_memory_required: str | None = None
cpu_memory_required: str | None = None
Expand All @@ -298,7 +301,7 @@ class ResourceConstraints(BaseModel):


class HealthCheck(BaseModel):
"""Health check configuration for services"""
"""Health check configuration for service_definitions"""

endpoint: str | None = None
interval_seconds: int = 30
Expand All @@ -307,7 +310,7 @@ class HealthCheck(BaseModel):


class ServiceDependency(BaseModel):
"""Defines dependencies between services"""
"""Defines dependencies between service_definitions"""

service_name: str
required: bool = True
Expand Down
8 changes: 4 additions & 4 deletions src/orchestration/provisioner.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ def singleton(cls, cache: Optional[Cache] = None):
return _system_provisioner

def get_configured_services(self) -> List[ServiceDefinition]:
"""Get all services configured for this provisioner"""
return self.config_reader.services
"""Get all service_definitions configured for this provisioner"""
return self.config_reader.service_definitions

def get_active_services(self) -> List[ServiceInformation]:
"""Get all currently active services"""
Expand Down Expand Up @@ -491,7 +491,7 @@ def _stop_service(
updated = True

try:
# Some services may not implement stop yet
# Some service_definitions may not implement stop yet
stop_fn = getattr(
service_instance,
"stop",
Expand Down Expand Up @@ -812,7 +812,7 @@ def target_iterator(

targets: List[ConfiguredServiceIdentifier] = []
if request.footprint_all_services:
for svc_def in self.config_reader.services:
for svc_def in self.config_reader.service_definitions:
for target in target_iterator(svc_def):
targets.append(target)

Expand Down
31 changes: 15 additions & 16 deletions tasks/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def show_host_resources(c, use_api=False, port=DEFAULT_PROVISIONER_PORT):

@task(namespace="dev", name="build-containers")
def build_containers(c, name=None):
"""Build Docker images by delegating to util.services."""
"""Build Docker images by delegating to util.service_definitions."""
svc.build_containers(name=name)


Expand Down Expand Up @@ -151,11 +151,6 @@ def _get_installed_gpu_drivers(c):
PROVISIONER_NETWORK = "provisioner_network"


def _ensure_provisioner_network(c):
"""Deprecated: kept for compatibility. Delegates to util.services."""
svc.ensure_provisioner_network()


@task(namespace="dev", name="start-provisioner-network")
def start_provisioner_network(c):
"""Create the shared docker network for provisioner containers."""
Expand All @@ -170,13 +165,13 @@ def stop_provisioner_network(c):

@task(namespace="dev", name="start-provisioner")
def start_provisioner_api(c, port=DEFAULT_PROVISIONER_PORT, restart=True):
"""Start the provisioner-api container via util.services."""
"""Start the provisioner-api container via util.service_definitions."""
svc.start_provisioner_api(port=port, restart=restart)


@task(namespace="dev", name="stop-provisioner-api")
def stop_provisioner_api(c):
"""Stop the provisioner-api container via util.services."""
"""Stop the provisioner-api container via util.service_definitions."""
svc.stop_provisioner_api()


Expand Down Expand Up @@ -276,7 +271,7 @@ def list_configured_services(c, port=DEFAULT_PROVISIONER_PORT):
print(f" {key}: {value}")

print("\n" + "=" * 80)
print(f"Total services: {len(services_data)}")
print(f"Total service_definitions: {len(services_data)}")
print("=" * 80 + "\n")

except requests.exceptions.RequestException as e:
Expand Down Expand Up @@ -441,7 +436,7 @@ def list_active_services(c, port=DEFAULT_PROVISIONER_PORT):
print(f" {key}: {value}")

print("\n" + "=" * 80)
print(f"Total services: {len(services_data)}")
print(f"Total service_definitions: {len(services_data)}")
print("=" * 80 + "\n")

except requests.exceptions.RequestException as e:
Expand Down Expand Up @@ -528,7 +523,9 @@ def update_services(c, service, port=DEFAULT_PROVISIONER_PORT):
print("=" * 80)
msg = result_data.get("message", "Service update request accepted")
print(f"\n{msg}")
print(f"\nRequested services ({len(services_to_update)}):")
print(
f"\nRequested service_definitions ({len(services_to_update)}):"
)
for i, service in enumerate(services_to_update, 1):
profile_info = f"@{service.profile}" if service.profile else ""
print(
Expand Down Expand Up @@ -561,7 +558,7 @@ def _docker_group_id():

@task(namespace="dev", name="start-provisioner-backend")
def start_provisioner_backend(c, restart=True):
"""Start the provisioner-backend container via util.services."""
"""Start the provisioner-backend container via util.service_definitions."""
try:
svc.validate_footprint_data_env()
except RuntimeError as e:
Expand All @@ -576,7 +573,9 @@ def start_provisioner_redis(
port=DEFAULT_PROVISIONER_REDIS_PORT,
restart=True,
):
"""Start a Redis container for the provisioner via util.services."""
"""
Start a Redis container for the provisioner via util.service_definitions.
"""
svc.start_provisioner_redis(port=port, restart=restart)


Expand Down Expand Up @@ -610,7 +609,7 @@ def start_provisioner(
svc.ensure_provisioner_network()
# Start Redis first so backend and API can connect
svc.start_provisioner_redis(port=redis_port, restart=restart)
# Then the backend worker/services
# Then the backend worker/service_definitions
svc.start_provisioner_backend(
restart=restart,
mount_source_dir=mount_source_dir,
Expand All @@ -626,13 +625,13 @@ def start_provisioner(

@task(namespace="dev", name="stop-provisioner-backend")
def stop_provisioner_backend(c):
"""Stop the provisioner-backend container via util.services."""
"""Stop the provisioner-backend container via util.service_definitions."""
svc.stop_provisioner_backend()


@task(namespace="dev", name="stop-provisioner-redis")
def stop_provisioner_redis(c):
"""Stop the provisioner-redis container via util.services."""
"""Stop the provisioner-redis container via util.service_definitions."""
svc.stop_provisioner_redis()


Expand Down
Loading