| 
 | 1 | +// Copyright (c) Microsoft Corporation.  | 
 | 2 | +// Licensed under the MIT License.  | 
 | 3 | + | 
 | 4 | +using System.Net;  | 
 | 5 | +using Azure.Mcp.Core.Commands;  | 
 | 6 | +using Azure.Mcp.Core.Extensions;  | 
 | 7 | +using Azure.Mcp.Core.Models.Option;  | 
 | 8 | +using Azure.Mcp.Tools.ManagedLustre.Options;  | 
 | 9 | +using Azure.Mcp.Tools.ManagedLustre.Options.FileSystem;  | 
 | 10 | +using Azure.Mcp.Tools.ManagedLustre.Services;  | 
 | 11 | +using Microsoft.Extensions.Logging;  | 
 | 12 | + | 
 | 13 | +namespace Azure.Mcp.Tools.ManagedLustre.Commands.FileSystem;  | 
 | 14 | + | 
 | 15 | +public sealed class FileSystemImportJobCreateCommand(ILogger<FileSystemImportJobCreateCommand> logger)  | 
 | 16 | +    : BaseManagedLustreCommand<FileSystemImportJobCreateOptions>(logger)  | 
 | 17 | +{  | 
 | 18 | +    private const string CommandTitle = "Create AMLFS Import Job";  | 
 | 19 | + | 
 | 20 | +    public override string Name => "create";  | 
 | 21 | + | 
 | 22 | +    public override string Description =>  | 
 | 23 | +        """  | 
 | 24 | +        Creates a manual import job for an Azure Managed Lustre (AMLFS) file system. The import job scans the linked HSM/Blob container and imports specified path prefixes (or all when omitted) honoring the chosen conflict resolution mode. Use to hydrate the AMLFS namespace or refresh content.  | 
 | 25 | +        """;  | 
 | 26 | + | 
 | 27 | +    public override string Title => CommandTitle;  | 
 | 28 | + | 
 | 29 | +    public override ToolMetadata Metadata => new()  | 
 | 30 | +    {  | 
 | 31 | +        Destructive = false,  | 
 | 32 | +        Idempotent = true,  | 
 | 33 | +        OpenWorld = true,  | 
 | 34 | +        ReadOnly = false,  | 
 | 35 | +        LocalRequired = false,  | 
 | 36 | +        Secret = false  | 
 | 37 | +    };  | 
 | 38 | + | 
 | 39 | +    protected override void RegisterOptions(Command command)  | 
 | 40 | +    {  | 
 | 41 | +        base.RegisterOptions(command);  | 
 | 42 | +        // Required common option  | 
 | 43 | +        command.Options.Add(OptionDefinitions.Common.ResourceGroup.AsRequired());  | 
 | 44 | +        // Service-specific options  | 
 | 45 | +        command.Options.Add(ManagedLustreOptionDefinitions.FileSystemOption);  | 
 | 46 | +        command.Options.Add(ManagedLustreOptionDefinitions.ImportPrefixesOption);  | 
 | 47 | +        command.Options.Add(ManagedLustreOptionDefinitions.ConflictResolutionModeOption);  | 
 | 48 | +        command.Options.Add(ManagedLustreOptionDefinitions.MaximumErrorsOption);  | 
 | 49 | +        command.Options.Add(ManagedLustreOptionDefinitions.JobNameOption);  | 
 | 50 | + | 
 | 51 | +        // Validation for conflict resolution mode (Skip|Fail) – consistent with validator style in SubnetSizeAskCommand  | 
 | 52 | +        command.Validators.Add(cmdResult =>  | 
 | 53 | +        {  | 
 | 54 | +            if (cmdResult.TryGetValue(ManagedLustreOptionDefinitions.ConflictResolutionModeOption, out var mode)  | 
 | 55 | +                && !string.IsNullOrWhiteSpace(mode)  | 
 | 56 | +                && !string.Equals(mode, "Skip", StringComparison.OrdinalIgnoreCase)  | 
 | 57 | +                && !string.Equals(mode, "Fail", StringComparison.OrdinalIgnoreCase))  | 
 | 58 | +            {  | 
 | 59 | +                cmdResult.AddError("Invalid conflict resolution mode. Allowed values: Skip, Fail.");  | 
 | 60 | +            }  | 
 | 61 | +        });  | 
 | 62 | +    }  | 
 | 63 | + | 
 | 64 | +    protected override FileSystemImportJobCreateOptions BindOptions(ParseResult parseResult)  | 
 | 65 | +    {  | 
 | 66 | +        var options = base.BindOptions(parseResult);  | 
 | 67 | +        options.FileSystem = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.FileSystemOption.Name);  | 
 | 68 | +        options.ResourceGroup ??= parseResult.GetValueOrDefault<string>(OptionDefinitions.Common.ResourceGroup.Name);  | 
 | 69 | +        var prefixes = parseResult.GetValueOrDefault<string[]>(ManagedLustreOptionDefinitions.ImportPrefixesOption.Name);  | 
 | 70 | +        if (prefixes == null || prefixes.Length == 0)  | 
 | 71 | +        {  | 
 | 72 | +            options.ImportPrefixes = new List<string> { "/" };  | 
 | 73 | +        }  | 
 | 74 | +        else  | 
 | 75 | +        {  | 
 | 76 | +            options.ImportPrefixes = prefixes.ToList();  | 
 | 77 | +        }  | 
 | 78 | +        var conflictMode = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.ConflictResolutionModeOption.Name);  | 
 | 79 | +        conflictMode = string.IsNullOrWhiteSpace(conflictMode)  | 
 | 80 | +            ? "Skip"  | 
 | 81 | +            : char.ToUpperInvariant(conflictMode[0]) + conflictMode.Substring(1).ToLowerInvariant();  | 
 | 82 | +        options.ConflictResolutionMode = conflictMode;  | 
 | 83 | +        options.MaximumErrors = parseResult.GetValueOrDefault<int?>(ManagedLustreOptionDefinitions.MaximumErrorsOption.Name) ?? -1;  | 
 | 84 | +        options.AdminStatus = "Active"; // Hard-coded since service no longer accepts parameter  | 
 | 85 | +        options.Name = parseResult.GetValueOrDefault<string>(ManagedLustreOptionDefinitions.JobNameOption.Name);  | 
 | 86 | +        return options;  | 
 | 87 | +    }  | 
 | 88 | + | 
 | 89 | +    public override async Task<CommandResponse> ExecuteAsync(CommandContext context, ParseResult parseResult)  | 
 | 90 | +    {  | 
 | 91 | +        var options = BindOptions(parseResult);  | 
 | 92 | +        try  | 
 | 93 | +        {  | 
 | 94 | +            if (!Validate(parseResult.CommandResult, context.Response).IsValid)  | 
 | 95 | +            {  | 
 | 96 | +                return context.Response;  | 
 | 97 | +            }  | 
 | 98 | + | 
 | 99 | +            var svc = context.GetService<IManagedLustreService>();  | 
 | 100 | +            var result = await svc.CreateImportJobAsync(  | 
 | 101 | +                options.Subscription!,  | 
 | 102 | +                options.ResourceGroup!,  | 
 | 103 | +                options.FileSystem!,  | 
 | 104 | +                options.Name,  | 
 | 105 | +                options.ImportPrefixes,  | 
 | 106 | +                options.ConflictResolutionMode!,  | 
 | 107 | +                options.MaximumErrors,  | 
 | 108 | +                options.Tenant,  | 
 | 109 | +                options.RetryPolicy);  | 
 | 110 | + | 
 | 111 | +            context.Response.Results = ResponseResult.Create(  | 
 | 112 | +                new FileSystemImportJobCreateResult(result),  | 
 | 113 | +                ManagedLustreJsonContext.Default.FileSystemImportJobCreateResult);  | 
 | 114 | +        }  | 
 | 115 | +        catch (Exception ex)  | 
 | 116 | +        {  | 
 | 117 | +            _logger.LogError(ex,  | 
 | 118 | +                "Error creating AMLFS import job. FileSystem: {FileSystem} ResourceGroup: {ResourceGroup} Options: {@Options}",  | 
 | 119 | +                options.FileSystem, options.ResourceGroup, options);  | 
 | 120 | +            HandleException(context, ex);  | 
 | 121 | +        }  | 
 | 122 | + | 
 | 123 | +        return context.Response;  | 
 | 124 | +    }  | 
 | 125 | + | 
 | 126 | +    protected override HttpStatusCode GetStatusCode(Exception ex) => ex switch  | 
 | 127 | +    {  | 
 | 128 | +        Azure.RequestFailedException reqEx => (HttpStatusCode)reqEx.Status,  | 
 | 129 | +        _ => base.GetStatusCode(ex)  | 
 | 130 | +    };  | 
 | 131 | + | 
 | 132 | +    internal record FileSystemImportJobCreateResult(Models.ImportJobInfo ImportJob);  | 
 | 133 | +}  | 
0 commit comments