From c35bc358b9daf9523c8ab1ed10cce23253ade6d9 Mon Sep 17 00:00:00 2001 From: Tsu-ba-me Date: Sun, 2 Jul 2023 03:11:19 -0400 Subject: [PATCH] fix(striker-ui-api): add prepare subnode network, hoist utils from config striker --- striker-ui-api/src/lib/buildJobData.ts | 14 ++ .../src/lib/fconfig/buildNetworkConfig.ts | 42 ++++++ .../src/lib/fconfig/buildNetworkLinkConfig.ts | 19 +++ striker-ui-api/src/lib/fconfig/index.ts | 2 + .../request_handlers/host/configStriker.ts | 82 ++---------- .../request_handlers/host/prepareNetwork.ts | 125 ++++++++++++++++++ .../lib/request_handlers/host/updateHost.ts | 2 + striker-ui-api/src/lib/varn.ts | 2 + striker-ui-api/src/types/ApiHost.d.ts | 14 +- 9 files changed, 225 insertions(+), 77 deletions(-) create mode 100644 striker-ui-api/src/lib/buildJobData.ts create mode 100644 striker-ui-api/src/lib/fconfig/buildNetworkConfig.ts create mode 100644 striker-ui-api/src/lib/fconfig/buildNetworkLinkConfig.ts create mode 100644 striker-ui-api/src/lib/fconfig/index.ts create mode 100644 striker-ui-api/src/lib/request_handlers/host/prepareNetwork.ts create mode 100644 striker-ui-api/src/lib/varn.ts diff --git a/striker-ui-api/src/lib/buildJobData.ts b/striker-ui-api/src/lib/buildJobData.ts new file mode 100644 index 00000000..925837cc --- /dev/null +++ b/striker-ui-api/src/lib/buildJobData.ts @@ -0,0 +1,14 @@ +export const buildJobData = ({ + entries, + getValue = (v) => String(v), +}: { + entries: T; + getValue?: (value: T[number][1]) => string; +}) => + entries + .reduce((previous, [key, value]) => { + previous += `${key}=${getValue(value)}\\n`; + + return previous; + }, '') + .trim(); diff --git a/striker-ui-api/src/lib/fconfig/buildNetworkConfig.ts b/striker-ui-api/src/lib/fconfig/buildNetworkConfig.ts new file mode 100644 index 00000000..7f8930ad --- /dev/null +++ b/striker-ui-api/src/lib/fconfig/buildNetworkConfig.ts @@ -0,0 +1,42 @@ +import { buildNetworkLinkConfig } from './buildNetworkLinkConfig'; +import { cvar } from '../varn'; + +export const buildNetworkConfig = ( + networks: InitializeStrikerNetworkForm[], + configStep = 2, +): FormConfigData => { + const { counters: ncounts, data: cdata } = networks.reduce<{ + counters: Record; + data: FormConfigData; + }>( + (previous, { interfaces, ipAddress, subnetMask, type }) => { + const { counters } = previous; + + counters[type] = counters[type] ? counters[type] + 1 : 1; + + const networkShortName = `${type}${counters[type]}`; + + previous.data = { + ...previous.data, + [cvar(configStep, `${networkShortName}_ip`)]: { + step: configStep, + value: ipAddress, + }, + [cvar(configStep, `${networkShortName}_subnet_mask`)]: { + step: configStep, + value: subnetMask, + }, + ...buildNetworkLinkConfig(networkShortName, interfaces), + }; + + return previous; + }, + { counters: {}, data: {} }, + ); + + Object.entries(ncounts).forEach(([ntype, ncount]) => { + cdata[cvar(1, `${ntype}_count`)] = { value: ncount }; + }); + + return cdata; +}; diff --git a/striker-ui-api/src/lib/fconfig/buildNetworkLinkConfig.ts b/striker-ui-api/src/lib/fconfig/buildNetworkLinkConfig.ts new file mode 100644 index 00000000..366cf9a2 --- /dev/null +++ b/striker-ui-api/src/lib/fconfig/buildNetworkLinkConfig.ts @@ -0,0 +1,19 @@ +import { cvar } from '../varn'; + +export const buildNetworkLinkConfig = ( + networkShortName: string, + interfaces: InitializeStrikerNetworkForm['interfaces'], + configStep = 2, +) => + interfaces.reduce((previous, iface, index) => { + if (iface) { + const { networkInterfaceMACAddress } = iface; + const linkNumber = index + 1; + + previous[ + cvar(configStep, `${networkShortName}_link${linkNumber}_mac_to_set`) + ] = { step: configStep, value: networkInterfaceMACAddress }; + } + + return previous; + }, {}); diff --git a/striker-ui-api/src/lib/fconfig/index.ts b/striker-ui-api/src/lib/fconfig/index.ts new file mode 100644 index 00000000..424ee6a9 --- /dev/null +++ b/striker-ui-api/src/lib/fconfig/index.ts @@ -0,0 +1,2 @@ +export * from './buildNetworkConfig'; +export * from './buildNetworkLinkConfig'; diff --git a/striker-ui-api/src/lib/request_handlers/host/configStriker.ts b/striker-ui-api/src/lib/request_handlers/host/configStriker.ts index 153b8f32..11cbc0f4 100644 --- a/striker-ui-api/src/lib/request_handlers/host/configStriker.ts +++ b/striker-ui-api/src/lib/request_handlers/host/configStriker.ts @@ -11,80 +11,11 @@ import { } from '../../consts'; import { getLocalHostUUID, job, variable } from '../../accessModule'; +import { buildJobData } from '../../buildJobData'; +import { buildNetworkConfig } from '../../fconfig'; import { sanitize } from '../../sanitize'; import { stderr, stdoutVar } from '../../shell'; - -const cvar = (configStepCount: number, fieldName: string) => - ['form', `config_step${configStepCount}`, fieldName, 'value'].join('::'); - -const buildNetworkLinkConfigs = ( - networkShortName: string, - interfaces: InitializeStrikerNetworkForm['interfaces'], - configStep = 2, -) => - interfaces.reduce((previous, iface, index) => { - if (iface) { - const { networkInterfaceMACAddress } = iface; - const linkNumber = index + 1; - - previous[ - cvar(configStep, `${networkShortName}_link${linkNumber}_mac_to_set`) - ] = { step: configStep, value: networkInterfaceMACAddress }; - } - - return previous; - }, {}); - -const buildNetworkConfigs = ( - networks: InitializeStrikerNetworkForm[], - configStep = 2, -): FormConfigData => { - const { counters: ncounts, data: cdata } = networks.reduce<{ - counters: Record; - data: FormConfigData; - }>( - (previous, { interfaces, ipAddress, subnetMask, type }) => { - const { counters } = previous; - - counters[type] = counters[type] ? counters[type] + 1 : 1; - - const networkShortName = `${type}${counters[type]}`; - - previous.data = { - ...previous.data, - [cvar(configStep, `${networkShortName}_ip`)]: { - step: configStep, - value: ipAddress, - }, - [cvar(configStep, `${networkShortName}_subnet_mask`)]: { - step: configStep, - value: subnetMask, - }, - ...buildNetworkLinkConfigs(networkShortName, interfaces), - }; - - return previous; - }, - { counters: {}, data: {} }, - ); - - Object.entries(ncounts).forEach(([ntype, ncount]) => { - cdata[cvar(1, `${ntype}_count`)] = { value: ncount }; - }); - - return cdata; -}; - -const configToJobData = ( - entries: [keyof FormConfigData, FormConfigData[keyof FormConfigData]][], -) => - entries - .reduce((previous, [key, { value }]) => { - previous += `${key}=${value}\\n`; - - return previous; - }, '') - .trim(); +import { cvar } from '../../varn'; export const configStriker: RequestHandler< unknown, @@ -182,7 +113,7 @@ export const configStriker: RequestHandler< [cvar(2, 'host_name')]: { step: 2, value: hostName }, [cvar(2, 'striker_password')]: { step: 2, value: adminPassword }, [cvar(2, 'striker_user')]: { step: 2, value: 'admin' }, - ...buildNetworkConfigs(networks), + ...buildNetworkConfig(networks), }; stdoutVar(configData, `Config data before initiating striker config: `); @@ -215,7 +146,10 @@ export const configStriker: RequestHandler< await job({ file: __filename, job_command: SERVER_PATHS.usr.sbin['anvil-configure-host'].self, - job_data: configToJobData(configEntries), + job_data: buildJobData({ + entries: configEntries, + getValue: ({ value }) => String(value), + }), job_name: 'configure::network', job_title: 'job_0001', job_description: 'job_0071', diff --git a/striker-ui-api/src/lib/request_handlers/host/prepareNetwork.ts b/striker-ui-api/src/lib/request_handlers/host/prepareNetwork.ts new file mode 100644 index 00000000..4c22a342 --- /dev/null +++ b/striker-ui-api/src/lib/request_handlers/host/prepareNetwork.ts @@ -0,0 +1,125 @@ +import assert from 'assert'; +import { RequestHandler } from 'express'; + +import { + REP_IPV4, + REP_IPV4_CSV, + REP_PEACEFUL_STRING, + REP_UUID, + SERVER_PATHS, +} from '../../consts'; + +import { job, variable } from '../../accessModule'; +import { buildJobData } from '../../buildJobData'; +import { buildNetworkConfig } from '../../fconfig'; +import { sanitize } from '../../sanitize'; +import { stderr, stdoutVar } from '../../shell'; +import { cvar } from '../../varn'; + +export const prepareNetwork: RequestHandler< + UpdateHostParams, + undefined, + PrepareNetworkRequestBody +> = async (request, response) => { + const { + body: { + dns: rDns, + gateway: rGateway, + hostName: rHostName, + gatewayInterface: rGatewayInterface, + networks = [], + } = {}, + params: { hostUUID }, + } = request; + + const dns = sanitize(rDns, 'string'); + const gateway = sanitize(rGateway, 'string'); + const hostName = sanitize(rHostName, 'string'); + const gatewayInterface = sanitize(rGatewayInterface, 'string'); + + try { + assert( + REP_UUID.test(hostUUID), + `Host UUID must be a valid UUIDv4; got [${hostUUID}]`, + ); + + assert( + REP_IPV4_CSV.test(dns), + `DNS must be a valid IPv4 CSV; got [${dns}]`, + ); + + assert( + REP_IPV4.test(gateway), + `Gateway must be a valid IPv4; got [${gateway}]`, + ); + + assert( + REP_PEACEFUL_STRING.test(hostName), + `Host name must be a peaceful string; got [${hostName}]`, + ); + + assert( + REP_PEACEFUL_STRING.test(gatewayInterface), + `Gateway interface must be a peaceful string; got [${gatewayInterface}]`, + ); + } catch (error) { + stderr(`Failed to assert value when prepare network; CAUSE: ${error}`); + + return response.status(400).send(); + } + + const configData: FormConfigData = { + [cvar(2, 'dns')]: { step: 2, value: dns }, + [cvar(2, 'gateway')]: { step: 2, value: gateway }, + [cvar(2, 'gateway_interface')]: { step: 2, value: gatewayInterface }, + [cvar(2, 'host_name')]: { step: 2, value: hostName }, + ...buildNetworkConfig(networks), + }; + + stdoutVar( + configData, + `Config data before prepare network on host ${hostUUID}: `, + ); + + const configEntries = Object.entries(configData); + + try { + for (const [ckey, cdetail] of configEntries) { + const { step = 1, value } = cdetail; + + const vuuid = await variable({ + file: __filename, + variable_default: '', + varaible_description: '', + variable_name: ckey, + variable_section: `config_step${step}`, + variable_source_uuid: hostUUID, + variable_source_table: 'hosts', + variable_value: value, + }); + + assert( + REP_UUID.test(vuuid), + `Not a UUIDv4 post insert or update of ${ckey} with [${cdetail}]`, + ); + } + + await job({ + file: __filename, + job_command: SERVER_PATHS.usr.sbin['anvil-configure-host'].self, + job_data: buildJobData({ + entries: configEntries, + getValue: ({ value }) => String(value), + }), + job_name: 'configure::network', + job_title: 'job_0001', + job_description: 'job_0071', + }); + } catch (error) { + stderr(`Failed to queue prepare network; CAUSE: ${error}`); + + return response.status(500).send(); + } + + return response.send(); +}; diff --git a/striker-ui-api/src/lib/request_handlers/host/updateHost.ts b/striker-ui-api/src/lib/request_handlers/host/updateHost.ts index 31948025..a07b3eb9 100644 --- a/striker-ui-api/src/lib/request_handlers/host/updateHost.ts +++ b/striker-ui-api/src/lib/request_handlers/host/updateHost.ts @@ -2,9 +2,11 @@ import { RequestHandler } from 'express'; import { buildBranchRequestHandler } from '../buildBranchRequestHandler'; import { configStriker } from './configStriker'; +import { prepareNetwork } from './prepareNetwork'; import { setHostInstallTarget } from './setHostInstallTarget'; export const updateHost: RequestHandler = buildBranchRequestHandler({ 'install-target': setHostInstallTarget as RequestHandler, + 'subnode-network': prepareNetwork as RequestHandler, striker: configStriker, }); diff --git a/striker-ui-api/src/lib/varn.ts b/striker-ui-api/src/lib/varn.ts new file mode 100644 index 00000000..d43cbcca --- /dev/null +++ b/striker-ui-api/src/lib/varn.ts @@ -0,0 +1,2 @@ +export const cvar = (step: number, name: string) => + ['form', `config_step${step}`, name, 'value'].join('::'); diff --git a/striker-ui-api/src/types/ApiHost.d.ts b/striker-ui-api/src/types/ApiHost.d.ts index e0f55bd1..9b35e8af 100644 --- a/striker-ui-api/src/types/ApiHost.d.ts +++ b/striker-ui-api/src/types/ApiHost.d.ts @@ -57,12 +57,12 @@ type InitializeStrikerNetworkForm = { type InitializeStrikerForm = { adminPassword: string; - domainName: string; - hostName: string; - hostNumber: number; dns: string; + domainName: string; gateway: string; gatewayInterface: string; + hostName: string; + hostNumber: number; networks: InitializeStrikerNetworkForm[]; organizationName: string; organizationPrefix: string; @@ -81,6 +81,14 @@ type PrepareHostRequestBody = { redhatUser: string; }; +type PrepareNetworkRequestBody = { + dns: string; + gateway: string; + gatewayInterface: string; + hostName: string; + networks: InitializeStrikerNetworkForm[]; +}; + type SetHostInstallTargetRequestBody = { isEnableInstallTarget: boolean; };