Skip to content
126 changes: 98 additions & 28 deletions docs/docs/concepts/backends.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,34 +579,6 @@ gcloud projects list --format="json(projectId)"
Using private subnets assumes that both the `dstack` server and users can access the configured VPC's private subnets.
Additionally, [Cloud NAT](https://cloud.google.com/nat/docs/overview) must be configured to provide access to external resources for provisioned instances.

## Hot Aisle

Log in to the SSH TUI as described in the [Hot Aisle Quick Start :material-arrow-top-right-thin:{ .external }](https://hotaisle.xyz/quick-start/).
Create a new team and generate an API key for the member in the team.

Then, go ahead and configure the backend:

<div editor-title="~/.dstack/server/config.yml">

```yaml
projects:
- name: main
backends:
- type: hotaisle
team_handle: hotaisle-team-handle
creds:
type: api_key
api_key: 9c27a4bb7a8e472fae12ab34.3f2e3c1db75b9a0187fd2196c6b3e56d2b912e1c439ba08d89e7b6fcd4ef1d3f
```

</div>

??? info "Required permissions"
The API key must have the following roles assigned:

* **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

## Lambda

Log into your [Lambda Cloud :material-arrow-top-right-thin:{ .external }](https://lambdalabs.com/service/gpu-cloud) account, click API keys in the sidebar, and then click the `Generate API key`
Expand Down Expand Up @@ -937,6 +909,104 @@ projects:

</div>

## AMD Developer Cloud
Log into your [AMD Developer Cloud :material-arrow-top-right-thin:{ .external }](https://amd.digitalocean.com/login) account. Click `API` in the sidebar and click the button `Generate New Token`.

Then, go ahead and configure the backend:

<div editor-title="~/.dstack/server/config.yml">

```yaml
projects:
- name: main
backends:
- type: amddevcloud
project_name: my-amd-project
creds:
type: api_key
api_key: dop_v1_71ea79a0c4bf2ffa70ac9d2a7b2689d2b41768567b22ebabe58a80066dcc5e92
```

</div>

??? info "Project Name"
**project_name** configuration is optional. If it is not provided, the default project is used.

??? info "Required permissions"
The API key must have the following scopes assigned:

* **account** - read
* **droplet** - create,read,update,delete,admin
* **project** - create,read,update,delete
* **regions** - read
* **sizes** - read
* **ssh_key** - create,read,update,delete



## Digital Ocean
Log into your [Digital Ocean :material-arrow-top-right-thin:{ .external }](https://cloud.digitalocean.com/login) account. Click `API` in the sidebar and click the button `Generate New Token`.

Then, go ahead and configure the backend:

<div editor-title="~/.dstack/server/config.yml">

```yaml
projects:
- name: main
backends:
- type: digitalocean
project_name: my-digital-ocean-project
creds:
type: api_key
api_key: dop_v1_71ea79a0c4bf2ffa70ac9d2a7b2689d2b41768567b22ebabe58a80066dcc5e92
```

</div>

??? info "Project Name"
**project_name** configuration is optional. If it is not provided, the default project is used.

??? info "Required permissions"
The API key must have the following scopes assigned:

* **account** - read
* **droplet** - create,read,update,delete,admin
* **project** - create,read,update,delete
* **regions** - read
* **sizes** - read
* **ssh_key** - create,read,update,delete


## Hot Aisle

Log in to the SSH TUI as described in the [Hot Aisle Quick Start :material-arrow-top-right-thin:{ .external }](https://hotaisle.xyz/quick-start/).
Create a new team and generate an API key for the member in the team.

Then, go ahead and configure the backend:

<div editor-title="~/.dstack/server/config.yml">

```yaml
projects:
- name: main
backends:
- type: hotaisle
team_handle: hotaisle-team-handle
creds:
type: api_key
api_key: 9c27a4bb7a8e472fae12ab34.3f2e3c1db75b9a0187fd2196c6b3e56d2b912e1c439ba08d89e7b6fcd4ef1d3f
```

</div>

??? info "Required permissions"
The API key must have the following roles assigned:

* **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


## CloudRift

Log into your [CloudRift :material-arrow-top-right-thin:{ .external }](https://console.cloudrift.ai/) console, click `API Keys` in the sidebar and click the button to create a new API key.
Expand Down
67 changes: 51 additions & 16 deletions docs/docs/reference/server/config.yml.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,6 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
type:
required: true

##### `projects[n].backends[type=hotaisle]` { #hotaisle data-toc-label="hotaisle" }

#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleBackendConfigWithCreds
overrides:
show_root_heading: false
type:
required: true
item_id_prefix: hotaisle-

###### `projects[n].backends[type=hotaisle].creds` { #hotaisle-creds data-toc-label="creds" }

#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleAPIKeyCreds
overrides:
show_root_heading: false
type:
required: true

##### `projects[n].backends[type=lambda]` { #lambda data-toc-label="lambda" }

Expand Down Expand Up @@ -332,6 +316,57 @@ to configure [backends](../../concepts/backends.md) and other [server-level sett
type:
required: true

##### `projects[n].backends[type=amddevcloud]` { #amddevcloud data-toc-label="amddevcloud" }

#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanBackendConfigWithCreds
overrides:
show_root_heading: false
type:
required: true
item_id_prefix: amddevcloud-

###### `projects[n].backends[type=amddevcloud].creds` { #amddevcloud-creds data-toc-label="creds" }

#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanAPIKeyCreds
overrides:
show_root_heading: false
type:
required: true

##### `projects[n].backends[type=digitalocean]` { #digitalocean data-toc-label="digitalocean" }

#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanBackendConfigWithCreds
overrides:
show_root_heading: false
type:
required: true
item_id_prefix: digitalocean-

###### `projects[n].backends[type=digitalocean].creds` { #digitalocean-creds data-toc-label="creds" }

#SCHEMA# dstack._internal.core.backends.digitalocean_base.models.BaseDigitalOceanAPIKeyCreds
overrides:
show_root_heading: false
type:
required: true

##### `projects[n].backends[type=hotaisle]` { #hotaisle data-toc-label="hotaisle" }

#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleBackendConfigWithCreds
overrides:
show_root_heading: false
type:
required: true
item_id_prefix: hotaisle-

###### `projects[n].backends[type=hotaisle].creds` { #hotaisle-creds data-toc-label="creds" }

#SCHEMA# dstack._internal.core.backends.hotaisle.models.HotAisleAPIKeyCreds
overrides:
show_root_heading: false
type:
required: true

##### `projects[n].backends[type=cloudrift]` { #cloudrift data-toc-label="cloudrift" }

#SCHEMA# dstack._internal.core.backends.cloudrift.models.CloudRiftBackendConfigWithCreds
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ dependencies = [
"python-multipart>=0.0.16",
"filelock",
"psutil",
"gpuhunt==0.1.7",
"gpuhunt==0.1.8",
"argcomplete>=3.5.0",
"ignore-python>=0.2.0",
"orjson",
Expand Down
1 change: 1 addition & 0 deletions src/dstack/_internal/core/backends/amddevcloud/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This package contains the implementation for the AMDDevCloud backend.
16 changes: 16 additions & 0 deletions src/dstack/_internal/core/backends/amddevcloud/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from dstack._internal.core.backends.amddevcloud.compute import AMDDevCloudCompute
from dstack._internal.core.backends.digitalocean_base.backend import BaseDigitalOceanBackend
from dstack._internal.core.backends.digitalocean_base.models import BaseDigitalOceanConfig
from dstack._internal.core.models.backends.base import BackendType


class AMDDevCloudBackend(BaseDigitalOceanBackend):
TYPE = BackendType.AMDDEVCLOUD
COMPUTE_CLASS = AMDDevCloudCompute

def __init__(self, config: BaseDigitalOceanConfig, api_url: str):
self.config = config
self._compute = AMDDevCloudCompute(self.config, api_url=api_url, type=self.TYPE)

def compute(self) -> AMDDevCloudCompute:
return self._compute
5 changes: 5 additions & 0 deletions src/dstack/_internal/core/backends/amddevcloud/compute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from dstack._internal.core.backends.digitalocean_base.compute import BaseDigitalOceanCompute


class AMDDevCloudCompute(BaseDigitalOceanCompute):
pass
28 changes: 28 additions & 0 deletions src/dstack/_internal/core/backends/amddevcloud/configurator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from dstack._internal.core.backends.amddevcloud.backend import AMDDevCloudBackend
from dstack._internal.core.backends.digitalocean_base.api_client import DigitalOceanAPIClient
from dstack._internal.core.backends.digitalocean_base.backend import BaseDigitalOceanBackend
from dstack._internal.core.backends.digitalocean_base.configurator import (
BaseDigitalOceanConfigurator,
)
from dstack._internal.core.backends.digitalocean_base.models import AnyBaseDigitalOceanCreds
from dstack._internal.core.models.backends.base import (
BackendType,
)


class AMDDevCloudConfigurator(BaseDigitalOceanConfigurator):
TYPE = BackendType.AMDDEVCLOUD
BACKEND_CLASS = AMDDevCloudBackend
API_URL = "https://api-amd.digitalocean.com"

def get_backend(self, record) -> BaseDigitalOceanBackend:
config = self._get_config(record)
return AMDDevCloudBackend(config=config, api_url=self.API_URL)

def _validate_creds(self, creds: AnyBaseDigitalOceanCreds, project_name: Optional[str] = None):
api_client = DigitalOceanAPIClient(creds.api_key, self.API_URL)
api_client.validate_api_key()
if project_name:
api_client.validate_project_name(project_name)
2 changes: 2 additions & 0 deletions src/dstack/_internal/core/backends/base/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def get_catalog_offers(
provider = backend.value
if backend == BackendType.LAMBDA:
provider = "lambdalabs"
if backend == BackendType.AMDDEVCLOUD:
provider = "digitalocean"
q = requirements_to_query_filter(requirements)
q.provider = [provider]
offers = []
Expand Down
15 changes: 15 additions & 0 deletions src/dstack/_internal/core/backends/configurators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

_CONFIGURATOR_CLASSES: List[Type[Configurator]] = []

try:
from dstack._internal.core.backends.amddevcloud.configurator import AMDDevCloudConfigurator

_CONFIGURATOR_CLASSES.append(AMDDevCloudConfigurator)
except ImportError:
pass

try:
from dstack._internal.core.backends.aws.configurator import AWSConfigurator
Expand Down Expand Up @@ -47,6 +53,15 @@
except ImportError:
pass

try:
from dstack._internal.core.backends.digitalocean.configurator import (
DigitalOceanConfigurator,
)

_CONFIGURATOR_CLASSES.append(DigitalOceanConfigurator)
except ImportError:
pass

try:
from dstack._internal.core.backends.gcp.configurator import GCPConfigurator

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# DigitalOcean backend for dstack
16 changes: 16 additions & 0 deletions src/dstack/_internal/core/backends/digitalocean/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from dstack._internal.core.backends.digitalocean.compute import DigitalOceanCompute
from dstack._internal.core.backends.digitalocean_base.backend import BaseDigitalOceanBackend
from dstack._internal.core.backends.digitalocean_base.models import BaseDigitalOceanConfig
from dstack._internal.core.models.backends.base import BackendType


class DigitalOceanBackend(BaseDigitalOceanBackend):
TYPE = BackendType.DIGITALOCEAN
COMPUTE_CLASS = DigitalOceanCompute

def __init__(self, config: BaseDigitalOceanConfig, api_url: str):
self.config = config
self._compute = DigitalOceanCompute(self.config, api_url=api_url, type=self.TYPE)

def compute(self) -> DigitalOceanCompute:
return self._compute
5 changes: 5 additions & 0 deletions src/dstack/_internal/core/backends/digitalocean/compute.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from ..digitalocean_base.compute import BaseDigitalOceanCompute


class DigitalOceanCompute(BaseDigitalOceanCompute):
pass
31 changes: 31 additions & 0 deletions src/dstack/_internal/core/backends/digitalocean/configurator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import Optional

from dstack._internal.core.backends.base.configurator import BackendRecord
from dstack._internal.core.backends.digitalocean.backend import DigitalOceanBackend
from dstack._internal.core.backends.digitalocean_base.api_client import DigitalOceanAPIClient
from dstack._internal.core.backends.digitalocean_base.backend import BaseDigitalOceanBackend
from dstack._internal.core.backends.digitalocean_base.configurator import (
BaseDigitalOceanConfigurator,
)
from dstack._internal.core.backends.digitalocean_base.models import (
AnyBaseDigitalOceanCreds,
)
from dstack._internal.core.models.backends.base import (
BackendType,
)


class DigitalOceanConfigurator(BaseDigitalOceanConfigurator):
TYPE = BackendType.DIGITALOCEAN
BACKEND_CLASS = DigitalOceanBackend
API_URL = "https://api.digitalocean.com"

def get_backend(self, record: BackendRecord) -> BaseDigitalOceanBackend:
config = self._get_config(record)
return DigitalOceanBackend(config=config, api_url=self.API_URL)

def _validate_creds(self, creds: AnyBaseDigitalOceanCreds, project_name: Optional[str] = None):
api_client = DigitalOceanAPIClient(creds.api_key, self.API_URL)
api_client.validate_api_key()
if project_name:
api_client.validate_project_name(project_name)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This package contains the base classes for DigitalOcean and AMDDevCloud backends.
Loading
Loading