diff --git a/docs/docs/concepts/backends.md b/docs/docs/concepts/backends.md index 620d5723cb..9b46822e5f 100644 --- a/docs/docs/concepts/backends.md +++ b/docs/docs/concepts/backends.md @@ -891,6 +891,18 @@ projects: * **Owner role for the user** - Required for creating and managing SSH keys * **Operator role for the team** - Required for managing virtual machines within the team +??? info "Pricing" + `dstack` shows the hourly price for Hot Aisle instances. Some instances also require an upfront payment for a minimum reservation period, which is usually a few hours. You will be charged for the full minimum period even if you stop the instance early. + + See the Hot Aisle API for the minimum reservation period for each instance type: + +
+ + ```shell + $ curl -H "Authorization: Token $API_KEY" https://admin.hotaisle.app/api/teams/$TEAM_HANDLE/virtual_machines/available/ | jq ".[] | {gpus: .Specs.gpus, MinimumReservationMinutes}" + ``` + +
### CloudRift diff --git a/src/dstack/_internal/core/backends/hotaisle/api_client.py b/src/dstack/_internal/core/backends/hotaisle/api_client.py index c4608bd252..a3cc355fcd 100644 --- a/src/dstack/_internal/core/backends/hotaisle/api_client.py +++ b/src/dstack/_internal/core/backends/hotaisle/api_client.py @@ -76,14 +76,25 @@ def get_vm_state(self, vm_name: str) -> str: def terminate_virtual_machine(self, vm_name: str) -> None: url = f"{API_URL}/teams/{self.team_handle}/virtual_machines/{vm_name}/" - response = self._make_request("DELETE", url) + response = self._make_request( + "DELETE", + url, + params={ + "force": "true", # delete even if min reservation time not met + }, + ) if response.status_code == 404: logger.debug("Hot Aisle virtual machine %s not found", vm_name) return response.raise_for_status() def _make_request( - self, method: str, url: str, json: Optional[Dict[str, Any]] = None, timeout: int = 30 + self, + method: str, + url: str, + json: Optional[dict[str, Any]] = None, + params: Optional[dict[str, str]] = None, + timeout: int = 30, ) -> requests.Response: headers = { "accept": "application/json", @@ -97,5 +108,6 @@ def _make_request( url=url, headers=headers, json=json, + params=params, timeout=timeout, )