1c1750e6be
On satellite, remove all references to free_bandwidth column in nodes table. On storage node, remove references to AllocatedBandwidth and MinimumBandwidth and mark as deprecated. Protobuf message, NodeCapacity, is left intact for backwards compatibility. Once this is released to all satellites, we can drop the column from the DB. Change-Id: I2ff6c6537fc9008a0c5588e951afea58ede85838
267 lines
8.7 KiB
C#
267 lines
8.7 KiB
C#
using Microsoft.Deployment.WindowsInstaller;
|
|
using System;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.IO.Abstractions;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Storj
|
|
{
|
|
public class CustomActions
|
|
{
|
|
|
|
[CustomAction]
|
|
public static ActionResult ValidateIdentityDir(Session session)
|
|
{
|
|
string identityDir = session["IDENTITYDIR"];
|
|
|
|
try
|
|
{
|
|
new CustomActionRunner().ValidateIdentityDir(identityDir);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
// Identity dir is invalid
|
|
session["STORJ_IDENTITYDIR_VALID"] = e.Message;
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
// Identity dir is valid
|
|
session["STORJ_IDENTITYDIR_VALID"] = "1";
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
[CustomAction]
|
|
public static ActionResult ValidateWallet(Session session)
|
|
{
|
|
string wallet = session["STORJ_WALLET"];
|
|
|
|
try
|
|
{
|
|
new CustomActionRunner().ValidateWallet(wallet);
|
|
} catch (ArgumentException e)
|
|
{
|
|
// Wallet is invalid
|
|
session["STORJ_WALLET_VALID"] = e.Message;
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
// Wallet is valid
|
|
session["STORJ_WALLET_VALID"] = "1";
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
[CustomAction]
|
|
public static ActionResult ValidateStorageDir(Session session)
|
|
{
|
|
string storageDir = session["STORAGEDIR"];
|
|
|
|
try
|
|
{
|
|
new CustomActionRunner().ValidateStorageDir(storageDir);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
// Storage dir is invalid
|
|
session["STORJ_STORAGEDIR_VALID"] = e.Message;
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
// Storage dir is valid
|
|
session["STORJ_STORAGEDIR_VALID"] = "1";
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
[CustomAction]
|
|
public static ActionResult ValidateStorage(Session session)
|
|
{
|
|
string storageStr = session["STORJ_STORAGE"];
|
|
string storageDir = session["STORAGEDIR"];
|
|
|
|
try
|
|
{
|
|
new CustomActionRunner().ValidateStorage(storageStr, storageDir);
|
|
}
|
|
catch (ArgumentException e)
|
|
{
|
|
// Allocated Storage is invalid
|
|
session["STORJ_STORAGE_VALID"] = e.Message;
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
// Allocated Storage value is valid
|
|
session["STORJ_STORAGE_VALID"] = "1";
|
|
return ActionResult.Success;
|
|
}
|
|
|
|
[CustomAction]
|
|
public static ActionResult ExtractInstallDir(Session session)
|
|
{
|
|
string line = session["STORJ_SERVICE_COMMAND"];
|
|
session.Log($"ExtractInstallDir registry value: {line}");
|
|
|
|
string path = new CustomActionRunner().ExtractInstallDir(line);
|
|
session.Log($"ExtractInstallDir extracted path: {path}");
|
|
|
|
session["STORJ_INSTALLDIR"] = path;
|
|
return ActionResult.Success;
|
|
}
|
|
}
|
|
|
|
public class CustomActionRunner
|
|
{
|
|
public const long GB = 1000 * 1000 * 1000;
|
|
public const long TB = (long)1000 * 1000 * 1000 * 1000;
|
|
public const long MinFreeSpace = 550 * GB; // (500 GB + 10% overhead)
|
|
|
|
private readonly IFileSystem fs;
|
|
|
|
public CustomActionRunner() : this(fs: new FileSystem())
|
|
{
|
|
}
|
|
|
|
public CustomActionRunner(IFileSystem fs)
|
|
{
|
|
this.fs = fs;
|
|
}
|
|
|
|
public void ValidateIdentityDir(string identityDir)
|
|
{
|
|
if (string.IsNullOrEmpty(identityDir))
|
|
{
|
|
throw new ArgumentException("You must select an identity folder.");
|
|
}
|
|
|
|
if (!fs.Directory.Exists(identityDir))
|
|
{
|
|
throw new ArgumentException(string.Format("Folder '{0}' does not exist.", identityDir));
|
|
}
|
|
|
|
if (!fs.File.Exists(Path.Combine(identityDir, "ca.cert")))
|
|
{
|
|
throw new ArgumentException("File 'ca.cert' not found in the selected folder.");
|
|
}
|
|
|
|
if (!fs.File.Exists(Path.Combine(identityDir, "ca.key")))
|
|
{
|
|
throw new ArgumentException("File 'ca.key' not found in the selected folder.");
|
|
}
|
|
|
|
if (!fs.File.Exists(Path.Combine(identityDir, "identity.cert")))
|
|
{
|
|
throw new ArgumentException("File 'identity.cert' not found in the selected folder.");
|
|
}
|
|
|
|
if (!fs.File.Exists(Path.Combine(identityDir, "identity.key")))
|
|
{
|
|
throw new ArgumentException("File 'identity.key' not found in the selected folder.");
|
|
}
|
|
}
|
|
|
|
public void ValidateWallet(string wallet)
|
|
{
|
|
if (string.IsNullOrEmpty(wallet))
|
|
{
|
|
throw new ArgumentException("The payout address cannot be empty.");
|
|
}
|
|
|
|
if (!wallet.StartsWith("0x"))
|
|
{
|
|
throw new ArgumentException("The payout address must start with a '0x' prefix.");
|
|
}
|
|
|
|
// Remove 0x prefix
|
|
wallet = wallet.Substring(2);
|
|
|
|
if (wallet.Length != 40)
|
|
{
|
|
throw new ArgumentException("The payout address must have 40 characters after the '0x' prefix.");
|
|
}
|
|
|
|
// TODO validate address checksum
|
|
}
|
|
|
|
public void ValidateStorageDir(string storageDir)
|
|
{
|
|
if (string.IsNullOrEmpty(storageDir))
|
|
{
|
|
throw new ArgumentException("You must select a storage folder.");
|
|
}
|
|
|
|
IDirectoryInfo dir = fs.DirectoryInfo.FromDirectoryName(storageDir);
|
|
IDriveInfo drive = fs.DriveInfo.FromDriveName(dir.Root.FullName);
|
|
|
|
// TODO: Find a way to calculate the available free space + total size of existing pieces
|
|
if (drive.TotalSize < MinFreeSpace)
|
|
{
|
|
throw new ArgumentException(string.Format("The selected drive '{0}' has only {1:0.##} GB disk size. The minimum required is 550 GB.",
|
|
drive.Name, decimal.Divide(drive.TotalSize, GB)));
|
|
}
|
|
}
|
|
|
|
public void ValidateStorage(string storageStr, string storageDir)
|
|
{
|
|
if (string.IsNullOrEmpty(storageStr))
|
|
{
|
|
throw new ArgumentException("The value cannot be empty.");
|
|
}
|
|
|
|
if (!double.TryParse(storageStr, NumberStyles.Number, CultureInfo.CreateSpecificCulture("en-US"), out double storage))
|
|
{
|
|
throw new ArgumentException(string.Format("'{0}' is not a valid number.", storageStr));
|
|
}
|
|
|
|
if (storage < 0.5)
|
|
{
|
|
throw new ArgumentException("The allocated disk space cannot be less than 0.5 TB.");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(storageDir))
|
|
{
|
|
throw new ArgumentException("The storage directory cannot be empty");
|
|
}
|
|
|
|
long storagePlusOverhead;
|
|
try
|
|
{
|
|
storagePlusOverhead = Convert.ToInt64(storage * 1.1 * TB);
|
|
}
|
|
catch (OverflowException)
|
|
{
|
|
throw new ArgumentException(string.Format("{0} TB is too large value for allocated storage.", storage));
|
|
}
|
|
|
|
IDirectoryInfo dir = fs.DirectoryInfo.FromDirectoryName(storageDir);
|
|
IDriveInfo drive = fs.DriveInfo.FromDriveName(dir.Root.FullName);
|
|
|
|
// TODO: Find a way to calculate the available free space + total size of existing pieces
|
|
if (drive.TotalSize < storagePlusOverhead)
|
|
{
|
|
throw new ArgumentException(string.Format("The disk size ({0:0.##} TB) on the selected drive {1} is less than the allocated disk space plus the 10% overhead ({2:0.##} TB total).",
|
|
decimal.Divide(drive.TotalSize, TB), drive.Name, decimal.Divide(storagePlusOverhead, TB)));
|
|
}
|
|
}
|
|
|
|
public string ExtractInstallDir(string serviceCmd)
|
|
{
|
|
if (string.IsNullOrEmpty(serviceCmd))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Regex pattern = new Regex(@"--config-dir ""(?<installDir>.*)""");
|
|
Match match = pattern.Match(serviceCmd);
|
|
string installDir = match.Groups["installDir"].Value;
|
|
|
|
if (string.IsNullOrEmpty(installDir))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return installDir;
|
|
}
|
|
}
|
|
|
|
|
|
}
|