[#11] Add Network Snapshot
Signed-off-by: Pavel Gross <p.gross@yadro.com>
This commit is contained in:
parent
b69d22966f
commit
c988ff3c76
84 changed files with 2238 additions and 933 deletions
|
@ -1,93 +1,223 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using FrostFS.Container;
|
||||
using FrostFS.Container;
|
||||
using FrostFS.Netmap;
|
||||
using FrostFS.Object;
|
||||
using FrostFS.SDK.ClientV2.Interfaces;
|
||||
using FrostFS.SDK.Cryptography;
|
||||
using FrostFS.SDK.ModelsV2;
|
||||
using FrostFS.SDK.ModelsV2.Netmap;
|
||||
using FrostFS.Session;
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using static FrostFS.Netmap.NetworkConfig.Types;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Version = FrostFS.SDK.ModelsV2.Version;
|
||||
|
||||
namespace FrostFS.SDK.ClientV2;
|
||||
|
||||
public partial class Client: IFrostFSClient
|
||||
public class Client : IFrostFSClient
|
||||
{
|
||||
private GrpcChannel? _channel;
|
||||
private readonly ECDsa _key;
|
||||
public readonly OwnerId OwnerId;
|
||||
public readonly Version Version = new(2, 13);
|
||||
private bool isDisposed;
|
||||
|
||||
private readonly Dictionary<string, ulong> NetworkSettings = [];
|
||||
internal ClientEnvironment ClientCtx { get; set; }
|
||||
|
||||
private ContainerService.ContainerServiceClient? _containerServiceClient;
|
||||
private NetmapService.NetmapServiceClient? _netmapServiceClient;
|
||||
private ObjectService.ObjectServiceClient? _objectServiceClient;
|
||||
private SessionService.SessionServiceClient? _sessionServiceClient;
|
||||
|
||||
public static IFrostFSClient GetInstance(string key, string host)
|
||||
public static IFrostFSClient GetInstance(IOptions<ClientSettings> clientOptions, GrpcChannelOptions channelOptions)
|
||||
{
|
||||
return new Client(key, host);
|
||||
return new Client(clientOptions, channelOptions);
|
||||
}
|
||||
|
||||
private Client(string key, string host)
|
||||
/// <summary>
|
||||
/// For test only. Provide custom implementation or mock object to inject required logic instead of internal gRPC client.
|
||||
/// </summary>
|
||||
/// <param name="clientOptions">Global setting for client</param>
|
||||
/// <param name="channelOptions">Setting for gRPC cjannel</param>
|
||||
/// <param name="containerService">ContainerService.ContainerServiceClient implementation</param>
|
||||
/// <param name="netmapService">Netmap.NetmapService.NetmapServiceClient implementation</param>
|
||||
/// <param name="sessionService">Session.SessionService.SessionServiceClient implementation</param>
|
||||
/// <param name="objectService">Object.ObjectService.ObjectServiceClient implementation</param>
|
||||
/// <returns></returns>
|
||||
public static IFrostFSClient GetTestInstance(
|
||||
IOptions<ClientSettings> clientOptions,
|
||||
GrpcChannelOptions? channelOptions,
|
||||
NetmapService.NetmapServiceClient netmapService,
|
||||
SessionService.SessionServiceClient sessionService,
|
||||
ContainerService.ContainerServiceClient containerService,
|
||||
ObjectService.ObjectServiceClient objectService)
|
||||
{
|
||||
// TODO: Развязать клиент и реализацию GRPC
|
||||
_key = key.LoadWif();
|
||||
OwnerId = OwnerId.FromKey(_key);
|
||||
InitGrpcChannel(host);
|
||||
InitContainerClient();
|
||||
InitNetmapClient();
|
||||
InitObjectClient();
|
||||
InitSessionClient();
|
||||
return new Client(clientOptions, channelOptions, containerService, netmapService, sessionService, objectService);
|
||||
}
|
||||
|
||||
private Client(
|
||||
IOptions<ClientSettings> settings,
|
||||
GrpcChannelOptions? channelOptions,
|
||||
ContainerService.ContainerServiceClient containerService,
|
||||
NetmapService.NetmapServiceClient netmapService,
|
||||
SessionService.SessionServiceClient sessionService,
|
||||
ObjectService.ObjectServiceClient objectService)
|
||||
{
|
||||
var ecdsaKey = settings.Value.Key.LoadWif();
|
||||
OwnerId.FromKey(ecdsaKey);
|
||||
|
||||
ClientCtx = new ClientEnvironment(
|
||||
key: ecdsaKey,
|
||||
owner: OwnerId.FromKey(ecdsaKey),
|
||||
channel: InitGrpcChannel(settings.Value.Host, channelOptions),
|
||||
version: new Version(2, 13));
|
||||
|
||||
ClientCtx.ContainerService = new ContainerServiceProvider(containerService, ClientCtx);
|
||||
ClientCtx.NetmapService = new NetmapServiceProvider(netmapService, ClientCtx);
|
||||
ClientCtx.SessionService = new SessionServiceProvider(sessionService, ClientCtx);
|
||||
ClientCtx.ObjectService = new ObjectServiceProvider(objectService, ClientCtx);
|
||||
}
|
||||
|
||||
private Client(IOptions<ClientSettings> options, GrpcChannelOptions channelOptions)
|
||||
{
|
||||
var clientSettings = (options?.Value) ?? throw new ArgumentException("Options must be initialized");
|
||||
|
||||
clientSettings.Validate();
|
||||
|
||||
var ecdsaKey = clientSettings.Key.LoadWif();
|
||||
|
||||
var channel = InitGrpcChannel(clientSettings.Host, channelOptions);
|
||||
|
||||
ClientCtx = new ClientEnvironment(
|
||||
key: ecdsaKey,
|
||||
owner: OwnerId.FromKey(ecdsaKey),
|
||||
channel: channel,
|
||||
version: new Version(2, 13));
|
||||
|
||||
ClientCtx.ContainerService = new ContainerServiceProvider(new ContainerService.ContainerServiceClient(channel), ClientCtx);
|
||||
ClientCtx.NetmapService = new NetmapServiceProvider(new NetmapService.NetmapServiceClient(channel), ClientCtx);
|
||||
ClientCtx.SessionService = new SessionServiceProvider(new SessionService.SessionServiceClient(channel), ClientCtx);
|
||||
ClientCtx.ObjectService = new ObjectServiceProvider(new ObjectService.ObjectServiceClient(channel), ClientCtx);
|
||||
|
||||
CheckFrostFsVersionSupport();
|
||||
|
||||
InitNetworkInfoAsync();
|
||||
}
|
||||
|
||||
private async void InitNetworkInfoAsync()
|
||||
public void Dispose()
|
||||
{
|
||||
var info = await GetNetworkInfoAsync();
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
foreach (var param in info.Body.NetworkInfo.NetworkConfig.Parameters)
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && !isDisposed)
|
||||
{
|
||||
SetNetworksParam(param);
|
||||
ClientCtx.Dispose();
|
||||
isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetNetworksParam(Parameter param)
|
||||
{
|
||||
var key = Encoding.UTF8.GetString(param.Key.ToByteArray());
|
||||
public GrpcChannel Channel => ClientCtx.Channel;
|
||||
|
||||
var encodedValue = param.Value.ToByteArray();
|
||||
|
||||
ulong val = 0;
|
||||
for (var i = encodedValue.Length - 1; i >= 0; i--)
|
||||
{
|
||||
val = (val << 8) + encodedValue[i];
|
||||
}
|
||||
|
||||
NetworkSettings.Add(key, val);
|
||||
}
|
||||
|
||||
private async void CheckFrostFsVersionSupport()
|
||||
public Task<ModelsV2.Container> GetContainerAsync(ContainerId containerId, Context? ctx = null)
|
||||
{
|
||||
var localNodeInfo = await GetLocalNodeInfoAsync();
|
||||
if (!localNodeInfo.Version.IsSupported(Version))
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.GetContainerAsync(containerId, ctx!);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<ContainerId> ListContainersAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.ListContainersAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<ContainerId> CreateContainerAsync(ModelsV2.Container container, Context? ctx = null)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.CreateContainerAsync(container, ctx!);
|
||||
}
|
||||
|
||||
public Task DeleteContainerAsync(ContainerId containerId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ContainerService!.DeleteContainerAsync(containerId, ctx!);
|
||||
}
|
||||
|
||||
public Task<NetmapSnapshot> GetNetmapSnapshotAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.NetmapService!.GetNetmapSnapshotAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<ModelsV2.Netmap.NodeInfo> GetNodeInfoAsync(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
|
||||
}
|
||||
|
||||
public Task<ObjectHeader> GetObjectHeadAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.GetObjectHeadAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public Task<ModelsV2.Object> GetObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.GetObjectAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public Task<ObjectId> PutObjectAsync(PutObjectParameters putObjectParameters, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.PutObjectAsync(putObjectParameters, ctx!);
|
||||
}
|
||||
|
||||
public Task<ObjectId> PutSingleObjectAsync(ModelsV2.Object obj, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.PutSingleObjectAsync(obj, ctx!);
|
||||
}
|
||||
|
||||
public Task DeleteObjectAsync(ContainerId containerId, ObjectId objectId, Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.DeleteObjectAsync(containerId, objectId, ctx!);
|
||||
}
|
||||
|
||||
public IAsyncEnumerable<ObjectId> SearchObjectsAsync(
|
||||
ContainerId containerId,
|
||||
IEnumerable<ObjectFilter> filters,
|
||||
Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
return ClientCtx.ObjectService!.SearchObjectsAsync(containerId, filters, ctx!);
|
||||
}
|
||||
|
||||
public ObjectId CalculateObjectId(ObjectHeader header)
|
||||
{
|
||||
return ClientCtx.ObjectService!.CalculateObjectId(header);
|
||||
}
|
||||
|
||||
private async void CheckFrostFsVersionSupport(Context? ctx = default)
|
||||
{
|
||||
ValidateEnvironment(ref ctx);
|
||||
var localNodeInfo = await ClientCtx.NetmapService!.GetLocalNodeInfoAsync(ctx!);
|
||||
|
||||
if (!localNodeInfo.Version.IsSupported(ClientCtx.Version))
|
||||
{
|
||||
var msg = $"FrostFS {localNodeInfo.Version} is not supported.";
|
||||
Console.WriteLine(msg);
|
||||
throw new ApplicationException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitGrpcChannel(string host)
|
||||
|
||||
private void ValidateEnvironment(ref Context? ctx)
|
||||
{
|
||||
if (isDisposed)
|
||||
throw new Exception("Client is disposed.");
|
||||
|
||||
if (ClientCtx == null || !ClientCtx.Initialized)
|
||||
throw new Exception("Client is not initialized.");
|
||||
|
||||
ctx ??= new Context();
|
||||
}
|
||||
|
||||
private static GrpcChannel InitGrpcChannel(string host, GrpcChannelOptions? channelOptions)
|
||||
{
|
||||
Uri uri;
|
||||
try
|
||||
|
@ -97,49 +227,25 @@ public partial class Client: IFrostFSClient
|
|||
catch (UriFormatException e)
|
||||
{
|
||||
var msg = $"Host '{host}' has invalid format. Error: {e.Message}";
|
||||
Console.WriteLine(msg);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
ChannelCredentials grpcCredentials;
|
||||
switch (uri.Scheme)
|
||||
ChannelCredentials grpcCredentials = uri.Scheme switch
|
||||
{
|
||||
case "https":
|
||||
grpcCredentials = ChannelCredentials.SecureSsl;
|
||||
break;
|
||||
case "http":
|
||||
grpcCredentials = ChannelCredentials.Insecure;
|
||||
break;
|
||||
default:
|
||||
var msg = $"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.";
|
||||
Console.WriteLine(msg);
|
||||
throw new ArgumentException(msg);
|
||||
"https" => ChannelCredentials.SecureSsl,
|
||||
"http" => ChannelCredentials.Insecure,
|
||||
_ => throw new ArgumentException($"Host '{host}' has invalid URI scheme: '{uri.Scheme}'.")
|
||||
};
|
||||
|
||||
if (channelOptions != null)
|
||||
{
|
||||
return GrpcChannel.ForAddress(uri, channelOptions);
|
||||
}
|
||||
|
||||
_channel = GrpcChannel.ForAddress(uri, new GrpcChannelOptions
|
||||
{
|
||||
Credentials = grpcCredentials,
|
||||
HttpHandler = new System.Net.Http.HttpClientHandler()
|
||||
});
|
||||
}
|
||||
|
||||
private void InitContainerClient()
|
||||
{
|
||||
_containerServiceClient = new ContainerService.ContainerServiceClient(_channel);
|
||||
}
|
||||
|
||||
private void InitNetmapClient()
|
||||
{
|
||||
_netmapServiceClient = new NetmapService.NetmapServiceClient(_channel);
|
||||
}
|
||||
|
||||
private void InitObjectClient()
|
||||
{
|
||||
_objectServiceClient = new ObjectService.ObjectServiceClient(_channel);
|
||||
}
|
||||
|
||||
private void InitSessionClient()
|
||||
{
|
||||
_sessionServiceClient = new SessionService.SessionServiceClient(_channel);
|
||||
|
||||
return GrpcChannel.ForAddress(uri, new GrpcChannelOptions
|
||||
{
|
||||
Credentials = grpcCredentials,
|
||||
HttpHandler = new HttpClientHandler()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue