diff --git a/src/api/system_api.js b/src/api/system_api.js index c8b627b4f7..864cfdca8d 100644 --- a/src/api/system_api.js +++ b/src/api/system_api.js @@ -479,6 +479,7 @@ module.exports = { type: 'object', required: [ 'name', + 'system_id', 'roles', 'tiers', 'pools', @@ -495,6 +496,9 @@ module.exports = { name: { type: 'string', }, + system_id: { + type: 'string', + }, roles: { type: 'array', items: { diff --git a/src/server/node_services/node_server.js b/src/server/node_services/node_server.js index 16a640ee65..3348eb02d7 100644 --- a/src/server/node_services/node_server.js +++ b/src/server/node_services/node_server.js @@ -88,8 +88,8 @@ function _prepare_nodes_query(req) { } if (query.pools) { query.pools = new Set(_.map(query.pools, pool_name => { - const pool = req.system.pools_by_name[pool_name]; - return String(pool._id); + const pool = req.system.pools_by_name?.[pool_name]; + return String(pool?._id); })); } return query; diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index bb3135f31b..c950c59fef 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -89,7 +89,7 @@ async function create_account(req) { const resource = req.rpc_params.default_resource ? req.system.pools_by_name[req.rpc_params.default_resource] || (req.system.namespace_resources_by_name && req.system.namespace_resources_by_name[req.rpc_params.default_resource]) : - req.system.pools_by_name.backingstores; + req.system.pools_by_name[`${config.DEFAULT_POOL_NAME}-${system_store.data.systems[0]._id}`]; if (!resource) throw new RpcError('BAD_REQUEST', 'default resource doesn\'t exist'); if (resource.nsfs_config && resource.nsfs_config.fs_root_path && !req.rpc_params.nsfs_account_config) { throw new RpcError('Invalid account configuration - must specify nsfs_account_config when default resource is a namespace resource'); @@ -1384,7 +1384,7 @@ function validate_create_account_permissions(req) { function validate_create_account_params(req) { // find none-internal pools const has_non_internal_resources = (req.system && req.system.pools_by_name) ? - Object.values(req.system.pools_by_name).some(p => p.name !== 'backingstores') : + Object.values(req.system.pools_by_name).some(p => p.name !== `${config.DEFAULT_POOL_NAME}-${req.system._id}`) : false; if (req.rpc_params.name.unwrap() !== req.rpc_params.name.unwrap().trim()) { diff --git a/src/server/system_services/bucket_server.js b/src/server/system_services/bucket_server.js index 0d1b7b3f44..93d7114371 100644 --- a/src/server/system_services/bucket_server.js +++ b/src/server/system_services/bucket_server.js @@ -1323,7 +1323,7 @@ async function update_all_buckets_default_pool(req) { const pool_name = req.rpc_params.pool_name; const pool = req.system.pools_by_name[pool_name]; if (!pool) throw new RpcError('INVALID_POOL_NAME'); - const internal_pool = pool_server.get_optimal_non_default_pool_id(pool.system); + const internal_pool = pool_server.get_optimal_default_pool(pool.system); if (!internal_pool || !internal_pool._id) return; if (String(pool._id) === String(internal_pool._id)) return; const buckets_with_internal_pool = _.filter(req.system.buckets_by_name, bucket => diff --git a/src/server/system_services/pool_server.js b/src/server/system_services/pool_server.js index 10d3904e54..9bdb057d2f 100644 --- a/src/server/system_services/pool_server.js +++ b/src/server/system_services/pool_server.js @@ -1301,18 +1301,18 @@ function _is_cloud_pool(pool) { return Boolean(pool.cloud_pool_info); } -function _is_optimal_non_default_pool_id(pool) { - return Boolean(pool.name === config.DEFAULT_POOL_NAME); +function _is_optimal_default_pool(pool) { + return Boolean(pool.name === `${config.DEFAULT_POOL_NAME}-${pool.system._id}`); } -function get_optimal_non_default_pool_id(system) { - return system.pools_by_name[config.DEFAULT_POOL_NAME]; +function get_optimal_default_pool(system) { + return system.pools_by_name[`${config.DEFAULT_POOL_NAME}-${system._id}`]; } -async function get_optimal_non_mongo_pool_id() { +async function get_optimal_non_default_pool_id() { for (const pool of system_store.data.pools) { // skip backingstore_pool. - if (_is_optimal_non_default_pool_id(pool)) { + if (_is_optimal_default_pool(pool)) { continue; } const aggr_nodes = await nodes_client.instance().aggregate_nodes_by_pool([pool.name], pool.system._id); @@ -1329,7 +1329,7 @@ async function update_account_default_resource() { try { const system = system_store.data.systems[0]; if (system) { - const optimal_pool_id = await get_optimal_non_mongo_pool_id(); + const optimal_pool_id = await get_optimal_non_default_pool_id(); if (optimal_pool_id) { const updates = system_store.data.accounts @@ -1423,10 +1423,9 @@ exports.assign_pool_to_region = assign_pool_to_region; exports.scale_hosts_pool = scale_hosts_pool; exports.update_hosts_pool = update_hosts_pool; exports.update_cloud_pool = update_cloud_pool; -exports.get_optimal_non_mongo_pool_id = get_optimal_non_mongo_pool_id; exports.get_hosts_pool_agent_config = get_hosts_pool_agent_config; exports.update_issues_report = update_issues_report; exports.update_last_monitoring = update_last_monitoring; exports.calc_namespace_resource_mode = calc_namespace_resource_mode; exports.check_deletion_ownership = check_deletion_ownership; -exports.get_optimal_non_default_pool_id = get_optimal_non_default_pool_id; +exports.get_optimal_default_pool = get_optimal_default_pool; diff --git a/src/server/system_services/system_server.js b/src/server/system_services/system_server.js index ef46b53229..25d22f347e 100644 --- a/src/server/system_services/system_server.js +++ b/src/server/system_services/system_server.js @@ -189,7 +189,7 @@ function new_system_changes(req, name, owner_account_id) { let default_pool; if (config.DEFAULT_POOL_TYPE === 'HOSTS') { - const pool_name = config.DEFAULT_POOL_NAME; + const pool_name = `${config.DEFAULT_POOL_NAME}-${system._id}`; const fs_pool = pool_server.new_pool_defaults(pool_name, system._id, 'HOSTS', 'BLOCK_STORE_FS', owner_account_id); fs_pool.hosts_pool_info = { is_managed: false, host_count: 0 }; default_pool = fs_pool; @@ -577,6 +577,7 @@ async function read_system(req) { return { name: system.name, + system_id: system._id.toString(), objects: objects_sys.count.toJSNumber(), roles: _.map(system.roles_by_account, function(roles, account_id) { const account = system_store.data.get_by_id(account_id); diff --git a/src/test/integration_tests/internal/test_upgrade_scripts.js b/src/test/integration_tests/internal/test_upgrade_scripts.js index d98e4ad4b4..26c4599661 100644 --- a/src/test/integration_tests/internal/test_upgrade_scripts.js +++ b/src/test/integration_tests/internal/test_upgrade_scripts.js @@ -134,6 +134,7 @@ mocha.describe('test upgrade scripts', async function() { const system = system_store.data.systems[0]; const base = config.INTERNAL_STORAGE_POOL_NAME || config.DEFAULT_POOL_NAME || 'system-internal-storage-pool'; const internal_name = `${base}-${system._id}`; + const pool_name = `${config.DEFAULT_POOL_NAME}-${system._id}`; // Seed an internal mongo pool entry await system_store.make_changes({ @@ -159,6 +160,8 @@ mocha.describe('test upgrade scripts', async function() { const prefix_exists = system_store.data.pools.find(pool => pool.name.startsWith(base)); assert.strictEqual(exact_removed, undefined); assert.strictEqual(prefix_exists, undefined); + const exact_added = system_store.data.pools.find(pool => pool.name === pool_name); + assert.strictEqual(exact_added.name, pool_name); }); mocha.after(async function() { diff --git a/src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js b/src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js index a8f2b7f35c..e2968008ac 100755 --- a/src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js +++ b/src/upgrade/upgrade_scripts/5.20.0/remove_mongo_pool.js @@ -1,26 +1,120 @@ /* Copyright (C) 2025 NooBaa */ "use strict"; +const _ = require('lodash'); const config = require('../../../../config.js'); +const SensitiveString = require('../../../util/sensitive_string'); + async function run({ dbg, system_store }) { try { - dbg.log0(`Starting monogo pool delete...`); + dbg.log0(`Start: Monogo pool upgrade script...`); + dbg.log0(`Start: Create new default pool...`); + const pool_name = `${config.DEFAULT_POOL_NAME}-${system_store.data.systems[0]._id}`; + const default_pool = system_store.data.systems[0].pools_by_name[pool_name]; + if (!default_pool) { + await create_new_default_pool(dbg, pool_name, system_store); + dbg.log0(`End: Create new default pool Created...`); + } + const internal_mongo_pool = `${config.INTERNAL_STORAGE_POOL_NAME}-${system_store.data.systems[0]._id}`; dbg.log0(`Internal mongo pool id is : ${internal_mongo_pool}`); - const pool_ids = system_store.data.pools.filter(pool => pool.name === internal_mongo_pool); - if (pool_ids.length > 0) { - dbg.log0(`Removing default mongo pool: ${pool_ids[0]._id}`); - await system_store.make_changes({ remove: { pools: [pool_ids[0]._id] }}); + const mongo_pools = system_store.data.pools.filter(pool => (pool.mongo_info || pool.resource_type === 'INTERNAL')); + + dbg.log0(`Start: Update bucket default bucket pool with new default pool...`); + await update_buckets_default_pool(dbg, pool_name, mongo_pools[0], system_store); + dbg.log0(`End: Updated bucket default bucket pool with new default pool...`); + + if (mongo_pools.length > 0) { + dbg.log0(`Removing default mongo pool: ${mongo_pools[0]._id}`); + await system_store.make_changes({ remove: { pools: [mongo_pools[0]._id] }}); } else { dbg.log0('Removing mongo pool: Could not find the mongo pool...'); } + dbg.log0(`End: Monogo pool upgrade script...`); } catch (err) { dbg.error('Got error while removing mongo pool:', err); throw err; } } +async function update_buckets_default_pool(dbg, pool_name, mongo_pool, system_store) { + const pool = system_store.data.systems[0].pools_by_name[pool_name]; + if (!pool) { + dbg.error('INVALID_POOL_NAME:'); + throw new Error('INVALID_POOL_NAME'); + } + if (!mongo_pool || !mongo_pool._id) return; + if (String(pool._id) === String(mongo_pool._id)) return; + const buckets_with_internal_pool = _.filter(system_store.data.systems[0].buckets_by_name, bucket => + is_using_internal_storage(bucket, mongo_pool)); + if (!buckets_with_internal_pool.length) return; + + // The loop pushes one update per bucket + const updates = _.uniqBy([], '_id'); + for (const bucket of buckets_with_internal_pool) { + updates.push({ + _id: bucket.tiering.tiers[0].tier._id, + mirrors: [{ + _id: system_store.new_system_store_id(), + spread_pools: [pool._id] + }] + }); + } + dbg.log0(`Updating ${buckets_with_internal_pool.length} buckets to use ${pool_name} as default resource`); + await system_store.make_changes({ + update: { + tiers: updates + } + }); +} + +async function create_new_default_pool(dbg, pool_name, system_store) { + // TODO: UPDATE EMAIL + let account = system_store.get_account_by_email(new SensitiveString('admin@noobaa.io')); + if (!account) { + dbg.error('NO_SUCH_ACCOUNT', 'No such account email: admin@noobaa.io'); + // For testing + account = system_store.get_account_by_email(new SensitiveString(config.OPERATOR_ACCOUNT_EMAIL)); + } + const fs_pool = new_pool_defaults(system_store, pool_name, system_store.data.systems[0]._id, 'HOSTS', 'BLOCK_STORE_FS', account._id); + fs_pool.hosts_pool_info = { is_managed: false, host_count: 0 }; + const default_pool = fs_pool; + await system_store.make_changes({ + insert: { + pools: [default_pool] + } + }); +} + +function new_pool_defaults(system_store, name, system_id, resource_type, pool_node_type, owner_id) { + const now = Date.now(); + return { + _id: system_store.new_system_store_id(), + system: system_id, + name: name, + owner_id, + resource_type: resource_type, + pool_node_type: pool_node_type, + storage_stats: { + blocks_size: 0, + last_update: now - (2 * config.MD_GRACE_IN_MILLISECONDS) + }, + }; +} + +function is_using_internal_storage(bucket, internal_pool) { + if (!internal_pool || !internal_pool._id) return false; + const tiers = bucket.tiering && bucket.tiering.tiers; + if (!tiers || tiers.length !== 1) return false; + const mirrors = tiers[0].tier.mirrors; + if (mirrors.length !== 1) return false; + const spread_pools = mirrors[0].spread_pools; + if (spread_pools.length !== 1) return false; + + return String(spread_pools[0]._id) === String(internal_pool._id); +} + module.exports = { run, description: 'Noobaa no longer support mongo_pool backingstore, Remove mongo pool',