Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
/src/azure-cli/azure/cli/command_modules/extension/ @VeryEarly @Pan-Qi @jsntcy
/src/azure-cli/azure/cli/command_modules/feedback/ @VeryEarly @Pan-Qi @jsntcy
/src/azure-cli/azure/cli/command_modules/hdinsight/ @VeryEarly @Pan-Qi @jsntcy @aim-for-better
/src/azure-cli/azure/cli/command_modules/horizondb/ @VeryEarly @Pan-Qi @calvinhzy @jsntcy @arde0708
/src/azure-cli/azure/cli/command_modules/identity/ @isra-fel @notyashhh @xuming-ms @teresaritorto
/src/azure-cli/azure/cli/command_modules/iot/ @NoriZC @yanzhudd @teresaritorto @digimaun
/src/azure-cli/azure/cli/command_modules/keyvault/ @notyashhh @isra-fel @xuming-ms @teresaritorto
Expand Down
1 change: 1 addition & 0 deletions doc/sphinx/azhelpgen/doc_source_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"functionapp": "src/azure-cli/azure/cli/command_modules/appservice/_help.py",
"group": "src/azure-cli/azure/cli/command_modules/resource/_help.py",
"hdinsight": "src/azure-cli/azure/cli/command_modules/hdinsight/_help.py",
"horizondb": "src/azure-cli/azure/cli/command_modules/horizondb/_help.py",
"identity": "src/azure-cli/azure/cli/command_modules/identity/_help.py",
"iot": "src/azure-cli/azure/cli/command_modules/iot/_help.py",
"keyvault": "src/azure-cli/azure/cli/command_modules/keyvault/_help.py",
Expand Down
6 changes: 6 additions & 0 deletions scripts/live_test/CLITest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ stages:
Target: find
hdinsight:
Target: hdinsight
horizondb:
Target: horizondb
identity:
Target: identity
iot:
Expand Down Expand Up @@ -728,6 +730,8 @@ stages:
Target: find
hdinsight:
Target: hdinsight
horizondb:
Target: horizondb
identity:
Target: identity
iot:
Expand Down Expand Up @@ -1305,6 +1309,8 @@ stages:
Target: find
hdinsight:
Target: hdinsight
horizondb:
Target: horizondb
identity:
Target: identity
iot:
Expand Down
3 changes: 3 additions & 0 deletions src/azure-cli-core/azure/cli/core/commandIndex.latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@
"hdinsight": [
"azure.cli.command_modules.hdinsight"
],
"horizondb": [
"azure.cli.command_modules.horizondb"
],
"identity": [
"azure.cli.command_modules.identity"
],
Expand Down
1 change: 1 addition & 0 deletions src/azure-cli-core/azure/cli/core/profiles/_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class ResourceType(Enum): # pylint: disable=too-few-public-methods
MGMT_DATALAKE_STORE = ('azure.mgmt.datalake.store', None)
MGMT_DATAMIGRATION = ('azure.mgmt.datamigration', None)
MGMT_EVENTGRID = ('azure.mgmt.eventgrid', None)
MGMT_HORIZONDB = ('azure.mgmt.horizondb', None)
MGMT_MAPS = ('azure.mgmt.maps', None)
MGMT_POLICYINSIGHTS = ('azure.mgmt.policyinsights', None)
MGMT_RDBMS = ('azure.mgmt.rdbms', None)
Expand Down
36 changes: 36 additions & 0 deletions src/azure-cli/azure/cli/command_modules/horizondb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core import AzCommandsLoader
from azure.cli.core.commands import CliCommandType
from azure.cli.core.profiles import ResourceType
from azure.cli.command_modules.horizondb.utils._context import HorizonDBArgumentContext
from azure.cli.command_modules.horizondb.cluster_commands import load_command_table
from azure.cli.command_modules.horizondb._params import load_arguments
import azure.cli.command_modules.horizondb._help # pylint: disable=unused-import


# pylint: disable=import-outside-toplevel
class HorizonDBCommandsLoader(AzCommandsLoader):

def __init__(self, cli_ctx=None):

horizondb_custom = CliCommandType(
operations_tmpl='azure.cli.command_modules.horizondb.commands.custom_commands#{}')
super().__init__(
cli_ctx=cli_ctx,
resource_type=ResourceType.MGMT_HORIZONDB,
custom_command_type=horizondb_custom,
argument_context_cls=HorizonDBArgumentContext)

def load_command_table(self, args):
load_command_table(self, args)
return self.command_table

def load_arguments(self, command):
load_arguments(self, command)


COMMAND_LOADER_CLS = HorizonDBCommandsLoader
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.commands.client_factory import get_mgmt_service_client
from azure.cli.core.profiles import ResourceType
from azure.cli.core.auth.identity import get_environment_credential, AZURE_CLIENT_ID

# pylint: disable=import-outside-toplevel

RM_URI_OVERRIDE = 'AZURE_CLI_HORIZONDB_FLEXIBLE_RM_URI'
SUB_ID_OVERRIDE = 'AZURE_CLI_HORIZONDB_FLEXIBLE_SUB_ID'


def get_horizondb_management_client(cli_ctx, subscription_id=None, **_):
from os import getenv
from azure.mgmt.horizondb import HorizonDBMgmtClient
# Allow overriding resource manager URI using environment variable
# for testing purposes. Subscription id is also determined by environment
# variable.
rm_uri_override = getenv(RM_URI_OVERRIDE)
subscription = subscription_id if subscription_id is not None else getenv(SUB_ID_OVERRIDE)
if rm_uri_override:
client_id = getenv(AZURE_CLIENT_ID)
if client_id:
credentials = get_environment_credential()
else:
from msrest.authentication import Authentication # pylint: disable=import-error
credentials = Authentication()

return HorizonDBMgmtClient(
subscription_id=subscription,
base_url=rm_uri_override,
credential=credentials)
# Normal production scenario.
return get_mgmt_service_client(cli_ctx, HorizonDBMgmtClient, subscription_id=subscription)


def resource_client_factory(cli_ctx, subscription_id=None):
return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES, subscription_id=subscription_id)


def cf_horizondb_clusters(cli_ctx, _):
return get_horizondb_management_client(cli_ctx).horizon_db_clusters
44 changes: 44 additions & 0 deletions src/azure-cli/azure/cli/command_modules/horizondb/_help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# coding=utf-8
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from knack.help_files import helps # pylint: disable=unused-import

# pylint: disable=line-too-long, too-many-lines


helps['horizondb'] = """
type: group
short-summary: Manage Azure HorizonDB.
"""


helps['horizondb create'] = """
type: command
short-summary: Create a new Azure HorizonDB cluster.
examples:
- name: Create a new HorizonDB cluster.
text: az horizondb create --name examplecluster --resource-group exampleresourcegroup --location centralus --admin-user myadmin --admin-password examplepassword --version 17 --v-cores 4 --replica-count 3
- name: Create a HorizonDB cluster with zone placement policy.
text: az horizondb create --name examplecluster --resource-group exampleresourcegroup --location centralus --admin-user myadmin --admin-password examplepassword --version 17 --v-cores 4 --replica-count 3 --zone-placement-policy Strict
"""


helps['horizondb delete'] = """
type: command
short-summary: Delete an Azure HorizonDB cluster.
examples:
- name: Delete an Azure HorizonDB cluster.
text: az horizondb delete --name examplecluster --resource-group exampleresourcegroup
"""


helps['horizondb show'] = """
type: command
short-summary: Show details of an Azure HorizonDB cluster.
examples:
- name: Show details of an Azure HorizonDB cluster.
text: az horizondb show --name examplecluster --resource-group exampleresourcegroup
"""
84 changes: 84 additions & 0 deletions src/azure-cli/azure/cli/command_modules/horizondb/_params.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long
# pylint: disable=too-many-statements

from knack.arguments import CLIArgumentType
from azure.cli.core.commands.parameters import (
resource_group_name_type,
get_location_type,
tags_type,
get_enum_type)
from azure.cli.core.local_context import LocalContextAttribute, LocalContextAction


def load_arguments(self, _): # pylint: disable=too-many-statements, too-many-locals

# HorizonDB
# pylint: disable=too-many-locals, too-many-branches
def _horizondb_params():

yes_arg_type = CLIArgumentType(
options_list=['--yes', '-y'],
action='store_true',
help='Do not prompt for confirmation.'
)

cluster_name_arg_type = CLIArgumentType(
metavar='NAME',
options_list=['--name', '-n'],
id_part='name',
help="Name of the cluster. The name can contain only lowercase letters, numbers, and the hyphen (-) character. Minimum 3 characters and maximum 63 characters.",
local_context_attribute=LocalContextAttribute(
name='cluster_name',
actions=[LocalContextAction.SET, LocalContextAction.GET],
scopes=['horizondb']))

administrator_login_arg_type = CLIArgumentType(
options_list=['--admin-user', '-u'],
help='The administrator login name for the cluster.')

administrator_login_password_arg_type = CLIArgumentType(
options_list=['--admin-password', '-p'],
help='The administrator login password.')

version_arg_type = CLIArgumentType(
options_list=['--version', '-v'],
help='The version of the HorizonDb cluster.')

replica_count_arg_type = CLIArgumentType(
options_list=['--replica-count'],
type=int,
help='Number of replicas.')

v_cores_arg_type = CLIArgumentType(
options_list=['--v-cores'],
type=int,
help='Number of vCores.')

zone_placement_policy_arg_type = CLIArgumentType(
options_list=['--zone-placement-policy'],
arg_type=get_enum_type(['Strict', 'BestEffort']),
help='Defines how replicas are placed across availability zones.')

with self.argument_context('horizondb') as c:
c.argument('resource_group_name', arg_type=resource_group_name_type)
c.argument('cluster_name', arg_type=cluster_name_arg_type)

with self.argument_context('horizondb create') as c:
c.argument('location', arg_type=get_location_type(self.cli_ctx))
c.argument('tags', tags_type)
c.argument('administrator_login', arg_type=administrator_login_arg_type)
c.argument('administrator_login_password', arg_type=administrator_login_password_arg_type)
c.argument('version', arg_type=version_arg_type)
c.argument('replica_count', arg_type=replica_count_arg_type)
c.argument('v_cores', arg_type=v_cores_arg_type)
c.argument('zone_placement_policy', arg_type=zone_placement_policy_arg_type)

with self.argument_context('horizondb delete') as c:
c.argument('yes', arg_type=yes_arg_type)

_horizondb_params()
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from azure.cli.core.commands import CliCommandType
from azure.cli.command_modules.horizondb._client_factory import (
cf_horizondb_clusters)
from azure.cli.command_modules.horizondb.utils._transformers import (
table_transform_output)


# pylint: disable=too-many-locals, too-many-statements, line-too-long
def load_command_table(self, _):
# Flexible server SDKs:
horizondb_clusters_sdk = CliCommandType(
operations_tmpl='azure.mgmt.horizondb.operations#HorizonDbClustersOperations.{}',
client_factory=cf_horizondb_clusters
)

# commands
custom_commands = CliCommandType(
operations_tmpl='azure.cli.command_modules.horizondb.commands.custom_commands#{}')
with self.command_group('horizondb', horizondb_clusters_sdk,
custom_command_type=custom_commands,
client_factory=cf_horizondb_clusters) as g:
g.custom_command('create', 'horizondb_cluster_create', table_transformer=table_transform_output)
g.custom_command('delete', 'horizondb_cluster_delete')
Comment on lines +25 to +28
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

table_transform_output expects keys like 'primary endpoint', 'username', and 'password' to exist in the command result. The HorizonDB create/show responses are standard ARM resources (e.g., name, location, properties.*) and don't include these keys, so az horizondb create -o table will raise a KeyError. Update the table transformer (or the create command output shaping) to match the actual response fields (e.g., FQDN/readonly endpoint/version/vCores/replicaCount) and guard against missing keys.

Copilot uses AI. Check for mistakes.
g.show_command('show', 'get')
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

# pylint: disable=line-too-long, too-many-locals

from knack.log import get_logger
from azure.cli.core.util import CLIError, sdk_no_wait, user_confirmation

logger = get_logger(__name__)


def horizondb_cluster_create(client, resource_group_name, cluster_name, location,
administrator_login, administrator_login_password,
tags=None, version=None,
replica_count=None, v_cores=None,
zone_placement_policy=None,
no_wait=False):
from azure.mgmt.horizondb.models import HorizonDbCluster, HorizonDbClusterProperties

properties = HorizonDbClusterProperties(
administrator_login=administrator_login,
administrator_login_password=administrator_login_password,
version=version,
create_mode="Create",
replica_count=replica_count,
v_cores=v_cores,
zone_placement_policy=zone_placement_policy,
)

resource = HorizonDbCluster(
location=location,
tags=tags,
properties=properties,
)

return sdk_no_wait(no_wait, client.begin_create_or_update,
resource_group_name=resource_group_name,
cluster_name=cluster_name,
resource=resource)


def horizondb_cluster_delete(cmd, client, resource_group_name, cluster_name, no_wait=False, yes=False):
if not yes:
user_confirmation(
"Are you sure you want to delete the cluster '{0}' in resource group '{1}'".format(cluster_name,
resource_group_name), yes=yes)
try:
result = sdk_no_wait(no_wait, client.begin_delete,
resource_group_name=resource_group_name,
cluster_name=cluster_name)
if cmd.cli_ctx.local_context.is_on:
local_context_file = cmd.cli_ctx.local_context._get_local_context_file() # pylint: disable=protected-access
local_context_file.remove_option('horizondb', 'cluster_name')
except Exception as ex: # pylint: disable=broad-except
logger.error(ex)
raise CLIError(ex)
return result


def horizondb_cluster_list(client, resource_group_name=None):
if resource_group_name:
return client.list_by_resource_group(resource_group_name=resource_group_name)
return client.list_by_subscription()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# --------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------
Loading
Loading