Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,7 @@
"managedlustre_fs_sku_get",
"managedlustre_fs_blob_autoexport_get",
"managedlustre_fs_blob_autoimport_get",
"managedlustre_fs_blob_import_get",
"storage_account_get",
"storage_blob_container_get",
"storage_blob_get",
Expand Down Expand Up @@ -1511,7 +1512,8 @@
"storage_blob_container_create",
"managedlustre_fs_create",
"managedlustre_fs_blob_autoexport_create",
"managedlustre_fs_blob_autoimport_create"
"managedlustre_fs_blob_autoimport_create",
"managedlustre_fs_blob_import_create"
]
},
{
Expand Down Expand Up @@ -1549,7 +1551,7 @@
},
{
"name": "manage_azure_managed_lustre_sync_jobs",
"description": "Manage Azure Managed Lustre data synchronization jobs including canceling and deleting autoimport and autoexport jobs that sync data between Lustre filesystems and Azure Blob Storage.",
"description": "Manage Azure Managed Lustre data synchronization jobs including canceling and deleting autoimport, autoexport, and one-time import jobs that sync data between Lustre filesystems and Azure Blob Storage.",
"toolMetadata": {
"destructive": {
"value": true,
Expand Down Expand Up @@ -1580,7 +1582,9 @@
"managedlustre_fs_blob_autoexport_cancel",
"managedlustre_fs_blob_autoexport_delete",
"managedlustre_fs_blob_autoimport_cancel",
"managedlustre_fs_blob_autoimport_delete"
"managedlustre_fs_blob_autoimport_delete",
"managedlustre_fs_blob_import_cancel",
"managedlustre_fs_blob_import_delete"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changes:
- section: "Features Added"
description: "Add Azure Managed Lustre fs blob import management commands: managedlustre_fs_blob_import_create, managedlustre_fs_blob_import_get, managedlustre_fs_blob_import_cancel, managedlustre_fs_blob_import_delete"
32 changes: 32 additions & 0 deletions servers/Azure.Mcp.Server/docs/azmcp-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,38 @@ azmcp managedlustre fs blob autoimport delete --subscription <subscription> \
--resource-group <resource-group> \
--filesystem-name <filesystem-name> \
--job-name <job-name>

# Create a one-time import job for an Azure Managed Lustre filesystem
# ✅ Destructive | ❌ Idempotent | ❌ OpenWorld | ❌ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp managedlustre fs blob import create --subscription <subscription> \
--resource-group <resource-group> \
--filesystem-name <filesystem-name> \
[--job-name <job-name>] \
[--conflict-resolution-mode <Fail|Skip|OverwriteIfDirty|OverwriteAlways>] \
[--import-prefixes <prefix1> --import-prefixes <prefix2> ...] \
[--maximum-errors <number>]

# Get details of one-time import jobs for an Azure Managed Lustre filesystem
# Returns a specific job if job-name is provided, or lists all jobs if omitted
# ❌ Destructive | ✅ Idempotent | ❌ OpenWorld | ✅ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp managedlustre fs blob import get --subscription <subscription> \
--resource-group <resource-group> \
--filesystem-name <filesystem-name> \
[--job-name <job-name>]

# Cancel a running one-time import job for an Azure Managed Lustre filesystem
# ✅ Destructive | ✅ Idempotent | ❌ OpenWorld | ❌ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp managedlustre fs blob import cancel --subscription <subscription> \
--resource-group <resource-group> \
--filesystem-name <filesystem-name> \
--job-name <job-name>

# Delete a one-time import job for an Azure Managed Lustre filesystem
# ✅ Destructive | ✅ Idempotent | ❌ OpenWorld | ❌ ReadOnly | ❌ Secret | ❌ LocalRequired
azmcp managedlustre fs blob import delete --subscription <subscription> \
--resource-group <resource-group> \
--filesystem-name <filesystem-name> \
--job-name <job-name>
```

### Azure Native ISV Operations
Expand Down
5 changes: 5 additions & 0 deletions servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,11 @@ This file contains prompts used for end-to-end testing to ensure each tool is in
| managedlustre_fs_blob_autoimport_delete | Delete the autoimport job <job_name> for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_autoimport_get | Get the details of autoimport job <job_name> for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_autoimport_list | List all autoimport jobs for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_import_create | Create a one-time import job for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_import_get | Get the details of import job <job_name> for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_import_get | List all one-time import jobs for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_import_cancel | Cancel the one-time import job <job_name> for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_blob_import_delete | Delete the one-time import job <job_name> for the Azure Managed Lustre filesystem <filesystem_name> in resource group <resource_group_name> |
| managedlustre_fs_subnetsize_ask | Tell me how many IP addresses I need for an Azure Managed Lustre filesystem of size <filesystem_size> using the SKU <sku> |
| managedlustre_fs_subnetsize_validate | Validate if the network <subnet_id> can host Azure Managed Lustre filesystem of size <filesystem_size> using the SKU <sku> |
| managedlustre_fs_update | Update the maintenance window of the Azure Managed Lustre filesystem <filesystem_name> to <maintenance_window_day> at <maintenance_window_time> |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Commands;
using Azure.Mcp.Core.Extensions;
using Azure.Mcp.Core.Models.Option;
using Azure.Mcp.Tools.ManagedLustre.Options;
using Azure.Mcp.Tools.ManagedLustre.Options.FileSystem.ImportJob;
using Azure.Mcp.Tools.ManagedLustre.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.ManagedLustre.Commands.FileSystem.ImportJob;

public sealed class ImportJobCancelCommand(ILogger<ImportJobCancelCommand> logger)
: BaseManagedLustreCommand<ImportJobCancelOptions>(logger)
{
private const string CommandTitle = "Cancel Azure Managed Lustre Import Job";

private new readonly ILogger<ImportJobCancelCommand> _logger = logger;

public override string Id => "d3h5e7g9-1f4a-6d8e-0g2c-4f6a8d0f2e4g";

public override string Name => "cancel";

public override string Description =>
"""
Cancels a running import job for an Azure Managed Lustre filesystem. This stops the import operation and prevents further processing. The job cannot be resumed after cancellation.
Required options:
- filesystem-name: The name of the AMLFS filesystem
- job-name: Name of the import job to cancel
""";

public override string Title => CommandTitle;

public override ToolMetadata Metadata => new()
{
Destructive = true,
Idempotent = true,
OpenWorld = false,
ReadOnly = false,
LocalRequired = false,
Secret = false
};

protected override void RegisterOptions(Command command)
{
base.RegisterOptions(command);
command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired());
command.Options.Add(ManagedLustreOptionDefinitions.FileSystemNameOption.AsRequired());
command.Options.Add(ManagedLustreOptionDefinitions.JobNameOption.AsRequired());
}

protected override ImportJobCancelOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.ResourceGroup ??= parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);
options.FileSystemName = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.FileSystemNameOption.Name);
options.JobName = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.JobNameOption.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

try
{
var svc = context.GetService<IManagedLustreService>();

var cancelledJob = await svc.CancelImportJobAsync(
options.Subscription!,
options.ResourceGroup!,
options.FileSystemName!,
options.JobName!,
options.Tenant,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(new(options.JobName!, cancelledJob.Properties?.AdminStatus ?? "Unknown"), ManagedLustreJsonContext.Default.ImportJobCancelResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cancelling import job {JobName} for AMLFS filesystem {FileSystem}. Options: {@Options}",
options.JobName, options.FileSystemName, options);
HandleException(context, ex);
}

return context.Response;
}

internal record ImportJobCancelResult(string JobName, string Status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Azure.Mcp.Core.Commands;
using Azure.Mcp.Core.Extensions;
using Azure.Mcp.Core.Models.Option;
using Azure.Mcp.Tools.ManagedLustre.Options;
using Azure.Mcp.Tools.ManagedLustre.Options.FileSystem.ImportJob;
using Azure.Mcp.Tools.ManagedLustre.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
using Microsoft.Mcp.Core.Models.Command;
using Microsoft.Mcp.Core.Models.Option;

namespace Azure.Mcp.Tools.ManagedLustre.Commands.FileSystem.ImportJob;

public sealed class ImportJobCreateCommand(ILogger<ImportJobCreateCommand> logger)
: BaseManagedLustreCommand<ImportJobCreateOptions>(logger)
{
private const string CommandTitle = "Create Azure Managed Lustre Import Job";

private new readonly ILogger<ImportJobCreateCommand> _logger = logger;

public override string Id => "b1f3c5e7-9d2a-4b8f-6c3e-1a7b9d2f5e8c";

public override string Name => "create";

public override string Description =>
"""
Creates a one-time import job for an Azure Managed Lustre filesystem to import files from the linked blob storage container. The import job performs a one-time sync of data from the configured HSM blob container to the Lustre filesystem. Use this to import specific prefixes or all data from blob storage into the filesystem at a point in time.
Required options:
- filesystem-name: The name of the AMLFS filesystem
Optional options:
- job-name: Name for the import job (auto-generated if not provided)
- conflict-resolution-mode: How to handle conflicting files (Fail, Skip, OverwriteIfDirty, OverwriteAlways, default: Fail)
- import-prefixes: Blob prefixes to import (default: imports all data from root '/')
- maximum-errors: Maximum errors allowed before job failure (-1: infinite, 0: fail on first error, default: use service default)
""";

public override string Title => CommandTitle;

public override ToolMetadata Metadata => new()
{
Destructive = true,
Idempotent = false,
OpenWorld = false,
ReadOnly = false,
LocalRequired = false,
Secret = false
};

protected override void RegisterOptions(Command command)
{
base.RegisterOptions(command);
command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired());
command.Options.Add(ManagedLustreOptionDefinitions.FileSystemNameOption.AsRequired());
command.Options.Add(ManagedLustreOptionDefinitions.OptionalJobNameOption);
command.Options.Add(ManagedLustreOptionDefinitions.ConflictResolutionModeOption);
command.Options.Add(ManagedLustreOptionDefinitions.ImportPrefixesOption);
command.Options.Add(ManagedLustreOptionDefinitions.MaximumErrorsOption);
}

protected override ImportJobCreateOptions BindOptions(ParseResult parseResult)
{
var options = base.BindOptions(parseResult);
options.ResourceGroup ??= parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);
options.FileSystemName = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.FileSystemNameOption.Name);
options.JobName = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.OptionalJobNameOption.Name);
options.ConflictResolutionMode = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.ConflictResolutionModeOption.Name);
options.ImportPrefixes = parseResult.GetValueOrDefault<string[]>(ManagedLustreOptionDefinitions.ImportPrefixesOption.Name);
options.MaximumErrors = parseResult.GetValueOrDefault<long?>(ManagedLustreOptionDefinitions.MaximumErrorsOption.Name);
return options;
}

public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult, CancellationToken cancellationToken)
{
if (!Validate(parseResult.CommandResult, context.Response).IsValid)
{
return context.Response;
}

var options = BindOptions(parseResult);

try
{
var svc = context.GetService<IManagedLustreService>();

// Log the prefixes for debugging
if (options.ImportPrefixes != null && options.ImportPrefixes.Length > 0)
{
_logger.LogInformation("Import prefixes received: {Prefixes}", string.Join(", ", options.ImportPrefixes));
}
else
{
_logger.LogInformation("No import prefixes received, will import all data");
}

var job = await svc.CreateImportJobAsync(
options.Subscription!,
options.ResourceGroup!,
options.FileSystemName!,
options.JobName,
options.ConflictResolutionMode,
options.ImportPrefixes,
options.MaximumErrors,
options.Tenant,
options.RetryPolicy,
cancellationToken);

context.Response.Results = ResponseResult.Create(new(job), ManagedLustreJsonContext.Default.ImportJobCreateResult);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating import job for AMLFS filesystem {FileSystem}. Options: {@Options}",
options.FileSystemName, options);
HandleException(context, ex);
}

return context.Response;
}

internal record ImportJobCreateResult(string JobName);
}
Loading
Loading