Skip to content
46 changes: 46 additions & 0 deletions sunbeam-python/sunbeam/commands/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SPDX-FileCopyrightText: 2025 - Canonical Ltd
# SPDX-License-Identifier: Apache-2.0

import logging

import click
from rich.console import Console

from sunbeam.core.deployment import Deployment
from sunbeam.storage.registry import StorageBackendRegistry

LOG = logging.getLogger(__name__)
console = Console()

CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}


@click.group("storage", context_settings=CONTEXT_SETTINGS)
@click.pass_context
def storage(ctx):
"""Manage Cinder storage backends.

Provides commands to add, remove, configure and list storage backends.
Supports multiple backend types including Hitachi VSP and others.
"""
# Ensure we have a deployment object
if not hasattr(ctx, "obj") or not isinstance(ctx.obj, Deployment):
raise click.ClickException(
"Storage commands require a valid deployment context. "
"Please ensure sunbeam is properly initialized."
)


def register_storage_commands(deployment: Deployment) -> None:
"""Register storage backend commands with the storage group.

This function is called from main.py to register all storage backend
commands dynamically based on available backends.
"""
try:
registry = StorageBackendRegistry()
registry.register_cli_commands(storage, deployment)
LOG.debug("Storage backend commands registered successfully")
except Exception as e:
LOG.error(f"Failed to register storage backend commands: {e}")
# Don't raise here as we want the CLI to still work
42 changes: 42 additions & 0 deletions sunbeam-python/sunbeam/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,45 @@ def convert_retry_failure_as_result(retry_state: RetryCallState) -> Result:
return Result(ResultType.FAILED, str(retry_state.outcome.exception()))
else:
return Result(ResultType.FAILED)


def friendly_terraform_lock_retry_callback(retry_state: RetryCallState) -> Result:
"""Friendly retry callback for Terraform state lock exceptions.

Shows user-friendly messages during lock retries
instead of verbose Terraform output.
"""
from sunbeam.core.terraform import TerraformStateLockedException

if retry_state.outcome is not None:
exception = retry_state.outcome.exception()
if isinstance(exception, TerraformStateLockedException):
# Extract lock ID from the error message if possible
lock_id = "unknown"
error_str = str(exception)
if "ID:" in error_str:
try:
# Extract lock ID from Terraform output
lines = error_str.split("\n")
for line in lines:
if "ID:" in line:
lock_id = line.split("ID:")[1].strip()
break
except Exception:
LOG.debug(
"Failed to extract lock ID from Terraform output: %s",
error_str,
)
pass

return Result(
ResultType.FAILED,
f"Terraform state is locked (ID: {lock_id}). "
f"This usually resolves automatically. "
f"If it persists, use 'sunbeam plans unlock <plan>' to "
f"clear stale locks.",
)
else:
return Result(ResultType.FAILED, str(exception))
else:
return Result(ResultType.FAILED, "Operation failed after retries")
5 changes: 5 additions & 0 deletions sunbeam-python/sunbeam/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from sunbeam.commands import prepare_node as prepare_node_cmds
from sunbeam.commands import proxy as proxy_cmds
from sunbeam.commands import sso as sso_cmd
from sunbeam.commands import storage as storage_cmds
from sunbeam.commands import utils as utils_cmds
from sunbeam.core import deployments as deployments_jobs
from sunbeam.provider import commands as provider_cmds
Expand Down Expand Up @@ -113,6 +114,7 @@ def main():
cli.add_command(launch_cmds.launch)
cli.add_command(openrc_cmds.openrc)
cli.add_command(dasboard_url_cmds.dashboard_url)
cli.add_command(storage_cmds.storage)

# Add identity group
cli.add_command(identity_group)
Expand Down Expand Up @@ -157,6 +159,9 @@ def main():
juju.add_command(juju_cmds.register_controller)
juju.add_command(juju_cmds.unregister_controller)

# Register storage backend commands
storage_cmds.register_storage_commands(deployment)

# Register the features after all groups,commands are registered
deployment.get_feature_manager().register(cli, deployment)

Expand Down
9 changes: 9 additions & 0 deletions sunbeam-python/sunbeam/storage/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2025 - Canonical Ltd
# SPDX-License-Identifier: Apache-2.0

"""Sunbeam Storage Backends.

This module provides a pluggable storage backend system for Sunbeam.
"""

# Backends are loaded dynamically by the registry - no hardcoded imports needed
7 changes: 7 additions & 0 deletions sunbeam-python/sunbeam/storage/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2025 - Canonical Ltd
# SPDX-License-Identifier: Apache-2.0

"""Sunbeam Storage Backend Implementations.

This package contains implementations of various storage backends for Sunbeam.
"""
9 changes: 9 additions & 0 deletions sunbeam-python/sunbeam/storage/backends/dellsc/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2025 - Canonical Ltd
# SPDX-License-Identifier: Apache-2.0

"""Dell Storage Center backend for Sunbeam storage."""

from .backend import DellSCBackend, DellSCConfig
from .cli import DellscCLI

__all__ = ["DellSCBackend", "DellSCConfig", "DellscCLI"]
Loading
Loading