Sashimono reconfiguration support in message board and sagent (#205)

This commit is contained in:
Chalith Desaman
2022-11-30 17:10:14 +05:30
committed by GitHub
parent f69bd13557
commit ca381bdbd2
7 changed files with 433 additions and 14 deletions

View File

@@ -75,7 +75,7 @@ if [ -f /etc/systemd/system/$SASHIMONO_SERVICE.service ] && [ -d $SASHIMONO_BIN
&& echo "$evernode is already installed on your host. Use the 'evernode' command to manage your host." \
&& exit 1
[ "$1" != "uninstall" ] && [ "$1" != "status" ] && [ "$1" != "list" ] && [ "$1" != "update" ] && [ "$1" != "log" ] && [ "$1" != "applyssl" ] && [ "$1" != "transfer" ]\
[ "$1" != "uninstall" ] && [ "$1" != "status" ] && [ "$1" != "list" ] && [ "$1" != "update" ] && [ "$1" != "log" ] && [ "$1" != "applyssl" ] && [ "$1" != "transfer" ] && [ "$1" != "reconfig" ] \
&& echomult "$evernode host management tool
\nYour host is registered on $evernode.
\nSupported commands:
@@ -83,6 +83,7 @@ if [ -f /etc/systemd/system/$SASHIMONO_SERVICE.service ] && [ -d $SASHIMONO_BIN
\nlist - View contract instances running on this system
\nlog - Generate evernode log file.
\napplyssl - Apply new SSL certificates for contracts.
\reconfig - Change the host configuration.
\nupdate - Check and install $evernode software updates
\ntransfer - Initiate an $evernode transfer for your machine
\nuninstall - Uninstall and deregister from $evernode" \
@@ -221,6 +222,16 @@ function validate_inet_addr() {
return 0
}
function validate_positive_decimal() {
! [[ $1 =~ ^(0*[1-9][0-9]*(\.[0-9]+)?|0+\.[0-9]*[1-9][0-9]*)$ ]] && return 1
return 0
}
function validate_ws_url() {
! [[ $1 =~ ^(wss?:\/\/)([^\/|^:|^ ]{3,})(:([0-9]{1,5}))?$ ]] && return 1
return 0
}
function set_inet_addr() {
if $interactive ; then
@@ -390,17 +401,17 @@ function set_instance_alloc() {
while true ; do
read -p "Specify the total RAM in megabytes to distribute among all contract instances: " ramMB </dev/tty
! [[ $ramMB -gt 0 ]] && echo "Invalid amount." || break
! [[ $ramMB -gt 0 ]] && echo "Invalid RAM size." || break
done
while true ; do
read -p "Specify the total Swap in megabytes to distribute among all contract instances: " swapMB </dev/tty
! [[ $swapMB -gt 0 ]] && echo "Invalid amount." || break
! [[ $swapMB -gt 0 ]] && echo "Invalid swap size." || break
done
while true ; do
read -p "Specify the total disk space in megabytes to distribute among all contract instances: " diskMB </dev/tty
! [[ $diskMB -gt 0 ]] && echo "Invalid amount." || break
! [[ $diskMB -gt 0 ]] && echo "Invalid disk size." || break
done
alloc_ramKB=$(( ramMB * 1000 ))
@@ -428,7 +439,7 @@ function set_lease_amount() {
local amount=0
while true ; do
read -p "Specify the lease amount in EVRs for your contract instances (per moment charge): " amount </dev/tty
! [[ $amount =~ ^(0*[1-9][0-9]*(\.[0-9]+)?|0+\.[0-9]*[1-9][0-9]*)$ ]] && echo "Lease amount should be a positive numerical value greater than zero." || break
! validate_positive_decimal $amount && echo "Lease amount should be a positive numerical value greater than zero." || break
done
lease_amount=$amount
@@ -445,7 +456,7 @@ function set_rippled_server() {
while true ; do
read -p "Specify the rippled URL: " newURL </dev/tty
! [[ $newURL =~ ^(wss:\/\/.*)$ ]] && echo "Rippled URL must be a valid URL that starts with 'wss://' ." || break
! validate_ws_url $newURL && echo "Rippled URL must be a valid URL that starts with 'wss://' ." || break
done
rippled_server=$newURL
@@ -476,6 +487,7 @@ function set_transferee_address() {
function set_host_xrpl_secret() {
if $interactive; then
echomult "In order to register in Evernode you need to have an XRPL account with EVRs.\n"
local secret=''
while true ; do
read -p "Specify the XRPL account secret: " secret </dev/tty
@@ -712,6 +724,120 @@ function apply_ssl() {
done
}
function reconfig() {
[ "$EUID" -ne 0 ] && echo "Please run with root privileges (sudo)." && exit 1
local mb_user_id=$(id -u "$MB_XRPL_USER")
local mb_user_runtime_dir="/run/user/$mb_user_id"
echomult "\nStaring reconfiguration...\n"
local saconfig="$SASHIMONO_DATA/sa.cfg"
local max_instance_count=$(jq '.system.max_instance_count' $saconfig)
local max_cpu_us=$(jq '.system.max_cpu_us' $saconfig)
local max_mem_kbytes=$(jq '.system.max_mem_kbytes' $saconfig)
local max_swap_kbytes=$(jq '.system.max_swap_kbytes' $saconfig)
local max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig)
local mbconfig="$MB_XRPL_DATA/mb-xrpl.cfg"
local cfg_lease_amount=$(jq '.xrpl.leaseAmount' $mbconfig)
local cfg_rippled_server=$(jq -r '.xrpl.rippledServer' $mbconfig)
local update_sashi=0
( ( [[ $alloc_instcount -gt 0 ]] && [[ $max_instance_count != $alloc_instcount ]] ) ||
( [[ $alloc_cpu -gt 0 ]] && [[ $max_cpu_us != $alloc_cpu ]] ) ||
( [[ $alloc_ramKB -gt 0 ]] && [[ $max_mem_kbytes != $alloc_ramKB ]] ) ||
( [[ $alloc_swapKB -gt 0 ]] && [[ $max_swap_kbytes != $alloc_swapKB ]] ) ||
( [[ $alloc_diskKB -gt 0 ]] && [[ $max_storage_kbytes != $alloc_diskKB ]] ) ) &&
update_sashi=1
local update_mb=0
( ( [ ! -z "$rippled_server" ] && [[ $cfg_rippled_server != $rippled_server ]] ) ||
( [[ $lease_amount -gt 0 ]] && [[ $cfg_lease_amount != $lease_amount ]] ) ||
( [[ $alloc_instcount -gt 0 ]] && [[ $max_instance_count != $alloc_instcount ]] ) ) &&
update_mb=1
# Update only if changed
[ $update_sashi == 0 ] && [ $update_mb == 0 ] && echomult "Given values are already configured!\n" && exit 0
# Stop the services to stop any activities.
# Stop the sashimono service.
if [ $update_sashi == 1 ] ; then
echomult "Stopping the sashimono...\n"
systemctl stop $SASHIMONO_SERVICE
fi
# Stop the message board service.
if [ $update_sashi == 1 ] || [ $update_mb == 1 ] ; then
echomult "Stopping the message board...\n"
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user stop $MB_XRPL_SERVICE
fi
if [ $update_sashi == 1 ] ; then
echomult "Using allocation"
[[ $alloc_cpu -gt 0 ]] && echomult "$alloc_cpu Micro Sec CPU" || alloc_cpu=0
[[ $alloc_ramKB -gt 0 ]] && echomult "$(GB $alloc_ramKB) RAM" || alloc_ramKB=0
[[ $alloc_swapKB -gt 0 ]] && echomult "$(GB $alloc_swapKB) Swap" || alloc_swapKB=0
[[ $alloc_diskKB -gt 0 ]] && echomult "$(GB $alloc_diskKB) disk space" || alloc_diskKB=0
[[ $alloc_instcount -gt 0 ]] && echomult "Distributed among $alloc_instcount contract instances" || alloc_instcount=0
echomult "\nConfiguaring sashimono...\n"
! $SASHIMONO_BIN/sagent reconfig $SASHIMONO_DATA $alloc_instcount $alloc_cpu $alloc_ramKB $alloc_swapKB $alloc_diskKB &&
echomult "\nThere was an error in updating sashimono configuration.\n" && exit 1
# Update cgroup allocations.
( [ $alloc_cpu -gt 0 ] || [ $alloc_ramKB -gt 0 ] || [ $alloc_swapKB -gt 0 ] || [ $alloc_instcount -gt 0 ] ) &&
echomult "Updating the cgroup configuration...\n" &&
! $SASHIMONO_BIN/user-cgcreate.sh $SASHIMONO_DATA && echomult "\nError occured while upgrading cgroup allocations\n" && exit 1
# Update disk quotas.
if ( [ $alloc_diskKB -gt 0 ] || [ $alloc_instcount -gt 0 ] ) ; then
echomult "Updating the disk quotas...\n"
users=$(cut -d: -f1 /etc/passwd | grep "^$SASHIUSER_PREFIX" | sort)
readarray -t userarr <<<"$users"
sashiusers=()
for user in "${userarr[@]}"; do
[ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$SASHIUSER_PREFIX[0-9]+$ ]] && continue
sashiusers+=("$user")
done
max_storage_kbytes=$(jq '.system.max_storage_kbytes' $saconfig)
max_instance_count=$(jq '.system.max_instance_count' $saconfig)
disk=$(expr $max_storage_kbytes / $max_instance_count)
ucount=${#sashiusers[@]}
if [ $ucount -gt 0 ]; then
for user in "${sashiusers[@]}"; do
setquota -g -F vfsv0 "$user" "$disk" "$disk" 0 0 /
done
fi
fi
fi
if [ $update_mb == 1 ] ; then
[ ! -z "$rippled_server" ] && echomult "Using the rippled address '$rippled_server'."
[[ $lease_amount -gt 0 ]] && echomult "Using lease amount $lease_amount EVRs." || lease_amount=0
[[ $alloc_instcount -gt 0 ]] || alloc_instcount=0
echomult "\nConfiguaring message board...\n"
! sudo -u $MB_XRPL_USER MB_DATA_DIR=$MB_XRPL_DATA node $MB_XRPL_BIN reconfig $lease_amount $alloc_instcount $rippled_server &&
echo "There was an error in updating message board configuration." && exit 1
fi
# Start the sashimono service.
if [ $update_sashi == 1 ] ; then
echomult "Starting the sashimono...\n"
systemctl start $SASHIMONO_SERVICE
fi
# Start the message board service.
if [ $update_sashi == 1 ] || [ $update_mb == 1 ] ; then
echomult "Starting the message board...\n"
sudo -u "$MB_XRPL_USER" XDG_RUNTIME_DIR="$mb_user_runtime_dir" systemctl --user start $MB_XRPL_SERVICE
fi
}
# Begin setup execution flow --------------------
echo "Thank you for trying out $evernode!"
@@ -839,6 +965,30 @@ elif [ "$mode" == "log" ]; then
elif [ "$mode" == "applyssl" ]; then
apply_ssl $2 $3 $4
elif [ "$mode" == "reconfig" ]; then
alloc_cpu=${2} # CPU microsec to allocate for contract instances (max 1000000).
alloc_ramKB=${3} # RAM to allocate for contract instances.
alloc_swapKB=${4} # Swap to allocate for contract instances.
alloc_diskKB=${5} # Disk space to allocate for contract instances.
alloc_instcount=${6} # Total contract instance count.
lease_amount=${7} # Contract instance lease amount in EVRs.
rippled_server=${8} # Ripple URL
( [ -z $alloc_cpu ] || [ -z $alloc_ramKB ] || [ -z $alloc_swapKB ] || [ -z $alloc_diskKB ] || [ -z $alloc_instcount ] || [ -z $lease_amount ] ) &&
echomult "Invalid arguments.\n Usage: sagent reconfig <cpu microsec> <ram kbytes> <swap kbytes> <disk kbytes> <max instance count> <lease amount> <rippled server>" && exit 1
[ ! -z $alloc_cpu ] && [ $alloc_cpu != 0 ] && ( ! ( validate_positive_decimal $alloc_cpu && [[ $alloc_cpu -le 1000000 ]] ) ) && echo "Invalid cpu allocation." && exit 1
[ ! -z $alloc_ramKB ] && [ $alloc_ramKB != 0 ] && ! validate_positive_decimal $alloc_ramKB && echo "Invalid ram size." && exit 1
[ ! -z $alloc_swapKB ] && [ $alloc_swapKB != 0 ] && ! validate_positive_decimal $alloc_swapKB && echo "Invalid swap size." && exit 1
[ ! -z $alloc_diskKB ] && [ $alloc_diskKB != 0 ] && ! validate_positive_decimal $alloc_diskKB && echo "Invalid disk size." && exit 1
[ ! -z $alloc_instcount ] && [ $alloc_instcount != 0 ] && ! validate_positive_decimal $alloc_instcount && echo "Invalid instance count." && exit 1
[ ! -z $lease_amount ] && [ $lease_amount != 0 ] && ! validate_positive_decimal $lease_amount && echo "Invalid lease amount." && exit 1
[ ! -z $rippled_server ] && ! validate_ws_url $rippled_server && echo "Rippled URL must be a valid URL that starts with 'wss://' ." && exit 1
reconfig
echo "Successfully changed the configuration!"
fi
[ "$mode" != "uninstall" ] && check_installer_pending_finish

View File

@@ -51,6 +51,9 @@ async function main() {
else if (process.argv.length === 3 && process.argv[2] === 'upgrade') {
await new Setup().upgrade();
}
else if ((process.argv.length === 5 || process.argv.length === 6) && process.argv[2] === 'reconfig') {
await new Setup().changeConfig(process.argv[3], process.argv[5], process.argv[4]);
}
else if (process.argv[2] === 'help') {
console.log(`Usage:
node index.js - Run message board.
@@ -62,6 +65,7 @@ async function main() {
node index.js deregister - Deregister the host from Evernode.
node index.js reginfo - Display Evernode registration info.
node index.js upgrade - Upgrade message board data.
node index.js reconfig [leaseAmount] [totalInstanceCount] [rippledServer] - Update message board configuration.
node index.js help - Print help.`);
}
else {
@@ -76,7 +80,7 @@ async function main() {
console.log('Data dir: ' + appenv.DATA_DIR);
console.log('Using Sashimono cli: ' + appenv.SASHI_CLI_PATH);
const mb = new MessageBoard(appenv.CONFIG_PATH, appenv.SECRET_CONFIG_PATH, appenv.DB_PATH, appenv.SASHI_CLI_PATH, appenv.SASHI_DB_PATH);
const mb = new MessageBoard(appenv.CONFIG_PATH, appenv.SECRET_CONFIG_PATH, appenv.DB_PATH, appenv.SASHI_CLI_PATH, appenv.SASHI_DB_PATH, appenv.SASHI_CONFIG_PATH);
await mb.init();
}

View File

@@ -19,6 +19,7 @@ appenv = {
DB_TABLE_NAME: 'leases',
DB_UTIL_TABLE_NAME: 'util_data',
SASHI_DB_PATH: (appenv.IS_DEV_MODE ? "../build/" : path.join(appenv.DATA_DIR, '../')) + "sa.sqlite",
SASHI_CONFIG_PATH: (appenv.IS_DEV_MODE ? "../build/" : path.join(appenv.DATA_DIR, '../')) + "sa.cfg",
SASHI_TABLE_NAME: 'instances',
LAST_WATCHED_LEDGER: 'last_watched_ledger',
ACQUIRE_LEASE_TIMEOUT_THRESHOLD: 0.8,

View File

@@ -39,6 +39,13 @@ class ConfigHelper {
fs.writeFileSync(secretConfigPath, JSON.stringify(secretCfg, null, 2), { mode: 0o600 }); // Set file permission so only current user can read/write.
fs.writeFileSync(configPath, JSON.stringify(publicCfg, null, 2), { mode: 0o644 }); // Set file permission so only current user can read/write and others can read.
}
static readSashiConfig(sashiConfigPath) {
if (!fs.existsSync(sashiConfigPath))
throw `Sashimono configuration file does not exist at ${sashiConfigPath}`;
return JSON.parse(fs.readFileSync(sashiConfigPath).toString());
}
}
module.exports = {

View File

@@ -23,7 +23,7 @@ class MessageBoard {
#graceTimeoutRef = null;
#lastHaltedTime = null;
constructor(configPath, secretConfigPath, dbPath, sashiCliPath, sashiDbPath) {
constructor(configPath, secretConfigPath, dbPath, sashiCliPath, sashiDbPath, sashiConfigPath) {
this.configPath = configPath;
this.secretConfigPath = secretConfigPath;
this.leaseTable = appenv.DB_TABLE_NAME;
@@ -37,7 +37,8 @@ class MessageBoard {
this.sashiCli = new SashiCLI(sashiCliPath);
this.db = new SqliteDatabase(dbPath);
this.sashiDb = new SqliteDatabase(sashiDbPath);
this.sashiTable = appenv.SASHI_TABLE_NAME
this.sashiTable = appenv.SASHI_TABLE_NAME;
this.sashiConfigPath = sashiConfigPath;
}
async init() {
@@ -84,10 +85,14 @@ class MessageBoard {
// Catch up missed transactions based on the previously updated "last_watched_ledger" record (checkpoint).
await this.#catchupMissedLeases().catch(console.error);
// Load the sashimono config.
const sashiConfig = ConfigHelper.readSashiConfig(this.sashiConfigPath);
this.activeInstanceCount = this.expiryList.length;
console.log(`Active instance count: ${this.activeInstanceCount}`);
// Update the registry with the active instance count.
await this.hostClient.updateRegInfo(this.activeInstanceCount, this.cfg.version);
await this.hostClient.updateRegInfo(this.activeInstanceCount, this.cfg.version, sashiConfig.system.max_instance_count, null, null,
sashiConfig.system.max_cpu_us, Math.floor((sashiConfig.system.max_mem_kbytes + sashiConfig.system.max_swap_kbytes) / 1000),
Math.floor(sashiConfig.system.max_storage_kbytes / 1000));
this.db.close();
this.xrplApi.on(evernode.XrplApiEvents.LEDGER, async (e) => {
@@ -601,7 +606,7 @@ class MessageBoard {
// Burn the NFTs and recreate the offer and send back the lease amount back to the tenant.
await this.hostClient.expireLease(nfTokenId, tenantAddress).catch(console.error);
// We refresh the config here, So if the purchaserTargetPrice is updated by the purchaser service, the new value will be taken.
this.hostClient.refreshConfig();
await this.hostClient.refreshConfig();
const leaseAmount = this.cfg.xrpl.leaseAmount ? this.cfg.xrpl.leaseAmount : parseFloat(this.hostClient.config.purchaserTargetPrice);
await this.hostClient.offerLease(leaseIndex, leaseAmount, appenv.TOS_HASH).catch(console.error);
}

View File

@@ -185,6 +185,9 @@ class Setup {
const hostClient = new evernode.HostClient(acc.address, acc.secret);
await hostClient.connect();
if (hostClient.config.hostRegFee > (await hostClient.getEVRBalance()))
throw `EVR balance in the account is less than the registration fee (${hostClient.config.hostRegFee}EVRs).`;
// Sometimes we may get 'tecPATH_DRY' error from rippled when some servers in the testnet cluster
// haven't still updated the ledger. In such cases, we retry several times before giving up.
let attempts = 0;
@@ -311,8 +314,8 @@ class Setup {
}
}
// Initiate Host Machine Transfer.
// Initiate Host Machine Transfer.
async transfer(transfereeAddress) {
console.log("Transferring host...");
const acc = this.#getConfig().xrpl;
@@ -322,7 +325,162 @@ class Setup {
await hostClient.connect();
await hostClient.transfer(transfereeAddress);
await this.burnMintedNfts(hostClient.xrplAcc);
await hostClient.disconnect();
}
// Change the message board configurations.
async changeConfig(leaseAmount, rippledServer, totalInstanceCount) {
// Update the configuration.
const cfg = this.#getConfig();
if (leaseAmount && isNaN(leaseAmount))
throw 'Lease amount should be a number';
else if (rippledServer && !rippledServer.match(/^(wss?:\/\/)([^\/|^:|^ ]{3,})(:([0-9]{1,5}))?$/g))
throw 'Provided Rippled Server is invalid';
else if (totalInstanceCount && isNaN(totalInstanceCount))
throw 'Maximum instance count should be a number';
const leaseAmountParsed = leaseAmount ? parseInt(leaseAmount) : 0;
const totalInstanceCountParsed = totalInstanceCount ? parseInt(totalInstanceCount) : 0;
// Return if not changed.
if (!totalInstanceCount &&
(!leaseAmount || cfg.xrpl.leaseAmount == leaseAmount) &&
(!rippledServer || cfg.xrpl.rippledServer == leaseAmount))
return;
await this.recreateLeases(leaseAmountParsed, totalInstanceCountParsed, rippledServer, cfg);
if (leaseAmountParsed)
cfg.xrpl.leaseAmount = leaseAmountParsed;
if (rippledServer)
cfg.xrpl.rippledServer = rippledServer;
this.#saveConfig(cfg);
}
// Recreate unsold NFTs
async recreateLeases(leaseAmount, totalInstanceCount, rippledServer, existingCfg) {
// Get sold NFTs.
const db = new SqliteDatabase(appenv.DB_PATH);
const leaseTable = appenv.DB_TABLE_NAME;
db.open();
const leaseRecords = (await db.getValues(leaseTable).finally(() => { db.close() })).filter(i => (i.status === "Acquired" || i.status === "Extended"));
const soldCount = leaseRecords.length;
if (totalInstanceCount && soldCount > totalInstanceCount)
throw `There are ${soldCount} active instances, So max instance count cannot be less than that.`;
const acc = existingCfg.xrpl;
let xrplApi;
let hostClient;
async function initClients(rippledServer) {
setEvernodeDefaults(acc.registryAddress, rippledServer);
xrplApi = new evernode.XrplApi();
hostClient = new evernode.HostClient(acc.address, acc.secret, { xrplApi: xrplApi });
await xrplApi.connect();
await hostClient.connect();
}
async function deinitClients() {
await hostClient.disconnect();
await xrplApi.disconnect();
}
await initClients(acc.rippledServer);
// Get unsold NFTs.
const unsoldNfts = (await hostClient.xrplAcc.getNfts()).filter(n => n.URI.startsWith(evernode.EvernodeConstants.LEASE_NFT_PREFIX_HEX))
.map(n => { return { nfTokenId: n.NFTokenID, leaseIndex: evernode.UtilHelpers.decodeLeaseNftUri(n.URI).leaseIndex }; });
const unsoldCount = unsoldNfts.length;
// Return if not changed.
if (!leaseAmount && !rippledServer && (!totalInstanceCount || (soldCount + unsoldCount) == totalInstanceCount)) {
await deinitClients();
return;
}
async function getVacantLeaseIndexes(includeUnsold = true) {
let acquired = includeUnsold ? [] : unsoldNfts.map(n => n.leaseIndex);
let vacant = [];
for (const l of leaseRecords) {
try {
const tenantAddress = l.tenant_xrp_address;
const nfTokenId = l.container_name;
const nft = (await (new evernode.XrplAccount(tenantAddress, null, { xrplApi: xrplApi })).getNfts())?.find(n => n.NFTokenID == nfTokenId);
if (nft) {
const index = evernode.UtilHelpers.decodeLeaseNftUri(nft.URI).leaseIndex;
acquired.push(index);
}
} catch {
}
}
let i = 0;
while (vacant.length + acquired.length < totalInstanceCount) {
if (!acquired.includes(i))
vacant.push(i);
i++;
}
return vacant;
}
let nftsToBurn = [];
let nftIndexesToCreate = [];
// If lease amount is changed we need to burn all the unsold nfts
if ((leaseAmount && acc.leaseAmount !== leaseAmount) || (rippledServer && acc.rippledServer !== rippledServer)) {
nftsToBurn = unsoldNfts;
// If total instance count also changed decide the nfts that we need to create.
if (totalInstanceCount && (soldCount + unsoldCount) !== totalInstanceCount) {
// If less than current count, Create only first chuck of the burned nfts.
// If greater than current count, create burned nfts plus extra nfts that are needed.
if (totalInstanceCount < soldCount + unsoldCount) {
nftIndexesToCreate = nftsToBurn.map(n => n.leaseIndex).sort((a, b) => a - b).slice(0, totalInstanceCount - soldCount);
}
else {
nftIndexesToCreate = await getVacantLeaseIndexes();
}
}
}
// If only instance count is changed decide whether we need to add or burn comparing the current count and updated count.
else if (totalInstanceCount && (soldCount + unsoldCount) !== totalInstanceCount) {
if (totalInstanceCount < soldCount + unsoldCount) {
nftsToBurn = unsoldNfts.sort((a, b) => a.leaseIndex - b.leaseIndex).slice(totalInstanceCount - soldCount);
nftIndexesToCreate = [];
}
else {
nftsToBurn = [];
nftIndexesToCreate = await getVacantLeaseIndexes(false);
}
}
for (const nft of nftsToBurn) {
try {
await hostClient.expireLease(nft.nfTokenId);
}
catch (e) {
console.error(e);
}
}
// If rippled server is changed, create new nfts from new server.
if (rippledServer && rippledServer !== acc.rippledServer) {
await initClients(rippledServer);
}
for (const idx of nftIndexesToCreate) {
try {
await hostClient.offerLease(idx,
leaseAmount ? leaseAmount : (acc.leaseAmount ? acc.leaseAmount : parseFloat(this.hostClient.config.purchaserTargetPrice)),
appenv.TOS_HASH);
}
catch (e) {
console.error(e);
}
}
await deinitClients();
}
}

View File

@@ -21,6 +21,7 @@
std::cerr << "sagent new [data_dir] [host_addr] [registry_addr] [inst_count] [cpu_us] [ram_kbytes] [swap_kbytes] [disk_kbytes]\n"; \
std::cerr << "sagent run [data_dir]\n"; \
std::cerr << "sagent upgrade [data_dir]\n"; \
std::cerr << "sagent reconfig [data_dir] [inst_count] [cpu_us] [ram_kbytes] [swap_kbytes] [disk_kbytes]\n"; \
std::cerr << "Example: sagent run /etc/sashimono\n"; \
return -1; \
}
@@ -40,7 +41,8 @@ int parse_cmd(int argc, char **argv)
if ((conf::ctx.command == "new" && argc >= 2 && argc <= 12) ||
(conf::ctx.command == "run" && argc >= 2 && argc <= 3) ||
(conf::ctx.command == "upgrade" && argc >= 2 && argc <= 3) ||
(conf::ctx.command == "version" && argc == 2))
(conf::ctx.command == "version" && argc == 2) ||
(conf::ctx.command == "reconfig" && argc >= 2 && argc <= 8))
return 0;
}
@@ -208,6 +210,98 @@ int main(int argc, char **argv)
if (conf::write_config(conf::cfg) != 0)
return -1;
}
else if (conf::ctx.command == "reconfig")
{
conf::set_dir_paths(argv[0], (argc >= 3) ? argv[2] : "");
size_t inst_count = 0, cpu_us = 0, ram_kbytes = 0, swap_kbytes = 0, disk_kbytes = 0;
if (((argc >= 4) && (util::stoull(argv[3], inst_count) != 0)) ||
((argc >= 5) && (util::stoull(argv[4], cpu_us) != 0)) ||
((argc >= 6) && (util::stoull(argv[5], ram_kbytes) != 0)) ||
((argc >= 7) && (util::stoull(argv[6], swap_kbytes) != 0)) ||
((argc >= 8) && (util::stoull(argv[7], disk_kbytes) != 0)))
{
std::cerr << "Invalid Sashimono Agent config update args.\n";
std::cerr << inst_count << ", " << cpu_us << ", " << ram_kbytes << ", "
<< swap_kbytes << ", " << disk_kbytes << "\n";
return 1;
}
if (conf::init() != 0)
return 1;
// Return if not changed.
if ((inst_count == 0 || conf::cfg.system.max_instance_count == inst_count) &&
(cpu_us == 0 || conf::cfg.system.max_cpu_us == cpu_us) &&
(ram_kbytes == 0 || conf::cfg.system.max_mem_kbytes == ram_kbytes) &&
(swap_kbytes == 0 || conf::cfg.system.max_swap_kbytes == swap_kbytes) &&
(disk_kbytes == 0 || conf::cfg.system.max_storage_kbytes == disk_kbytes))
return 0;
salog::init();
if (hp::init() == -1)
return 1;
std::vector<hp::instance_info> instances;
hp::get_instance_list(instances);
hp::deinit();
// If there are active instances, do not allow reducing the resources per instance. Otherwise we allow adjusting resources.
if (inst_count != 0 && instances.size() > inst_count)
{
std::cerr << "There are " << instances.size() << " active instances, So max instance count cannot be less than that.\n";
return 1;
}
else if (instances.size() > 0)
{
size_t new_count = inst_count != 0 ? inst_count : conf::cfg.system.max_instance_count;
size_t new_cpu = cpu_us != 0 ? cpu_us : conf::cfg.system.max_cpu_us;
size_t new_ram = ram_kbytes != 0 ? ram_kbytes : conf::cfg.system.max_mem_kbytes;
size_t new_swap = swap_kbytes != 0 ? swap_kbytes : conf::cfg.system.max_swap_kbytes;
size_t new_disk = disk_kbytes != 0 ? disk_kbytes : conf::cfg.system.max_storage_kbytes;
if (new_cpu / new_count < conf::cfg.system.max_cpu_us / conf::cfg.system.max_instance_count)
{
std::cerr << "CPU per instance should be greater than " << conf::cfg.system.max_cpu_us / conf::cfg.system.max_instance_count << " Micro Sec.\n";
return 1;
}
else if (new_ram / new_count < conf::cfg.system.max_mem_kbytes / conf::cfg.system.max_instance_count)
{
std::cerr << "RAM per instance should be greater than " << conf::cfg.system.max_mem_kbytes / conf::cfg.system.max_instance_count << " KB.\n";
return 1;
}
else if (new_swap / new_count < conf::cfg.system.max_swap_kbytes / conf::cfg.system.max_instance_count)
{
std::cerr << "Swap per instance should be greater than " << conf::cfg.system.max_swap_kbytes / conf::cfg.system.max_instance_count << " KB.\n";
return 1;
}
else if (new_disk / new_count < conf::cfg.system.max_storage_kbytes / conf::cfg.system.max_instance_count)
{
std::cerr << "Storage per instance should be greater than " << conf::cfg.system.max_storage_kbytes / conf::cfg.system.max_instance_count << " KB.\n";
return 1;
}
}
if (inst_count > 0)
conf::cfg.system.max_instance_count = inst_count;
if (cpu_us > 0)
conf::cfg.system.max_cpu_us = cpu_us;
if (ram_kbytes > 0)
conf::cfg.system.max_mem_kbytes = ram_kbytes;
if (swap_kbytes > 0)
conf::cfg.system.max_swap_kbytes = swap_kbytes;
if (disk_kbytes > 0)
conf::cfg.system.max_storage_kbytes = disk_kbytes;
if (conf::write_config(conf::cfg) != 0)
return -1;
}
return 0;
}