mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-22 22:03:39 +03:00
B OpenNebula/one#5833: Fix serivce template parsing (#3167)
Signed-off-by: Victor Hansson <vhansson@opennebula.io>
This commit is contained in:
parent
8ac48b54d1
commit
997843633d
@ -89,10 +89,10 @@ const NAME = {
|
||||
type: INPUT_TYPES.TEXT,
|
||||
validation: string()
|
||||
.trim()
|
||||
.lowercase()
|
||||
.matches(/^[a-z0-9_]*$/, {
|
||||
.uppercase()
|
||||
.matches(/^[A-Z0-9_]*$/, {
|
||||
message:
|
||||
'Name must only contain lowercase alphanumeric characters and no spaces',
|
||||
'Name must only contain uppercase alphanumeric characters and no spaces',
|
||||
excludeEmptyString: true,
|
||||
})
|
||||
.required()
|
||||
|
@ -180,9 +180,6 @@ const RoleNetwork = ({ stepId, selectedRoleIndex }) => {
|
||||
if (index === fieldArrayIndex) {
|
||||
return { ...f, rowSelected: rowToggle, aliasSelected: false }
|
||||
}
|
||||
if (f.aliasIdx === fieldArrayIndex) {
|
||||
return { ...f, aliasIdx: -1 }
|
||||
}
|
||||
|
||||
return f
|
||||
})
|
||||
|
@ -129,6 +129,9 @@ const Steps = createSteps([General, Extra, RoleDefinition, RoleConfig], {
|
||||
}, []),
|
||||
false
|
||||
),
|
||||
vm_template_contents: reversedVmTc?.map(
|
||||
(content) => content?.remainingContent
|
||||
),
|
||||
MINMAXVMS: ServiceTemplate?.TEMPLATE?.BODY?.roles
|
||||
?.filter((role) => role != null)
|
||||
?.map((role) => ({
|
||||
@ -162,7 +165,7 @@ const Steps = createSteps([General, Extra, RoleDefinition, RoleConfig], {
|
||||
[ROLE_DEFINITION_ID]: roleDefinitionData,
|
||||
[ROLE_CONFIG_ID]: { ...roleConfigData },
|
||||
},
|
||||
{ stripUnknown: true }
|
||||
{ stripUnknown: false }
|
||||
)
|
||||
|
||||
return knownTemplate
|
||||
@ -178,8 +181,11 @@ const Steps = createSteps([General, Extra, RoleDefinition, RoleConfig], {
|
||||
|
||||
const getVmTemplateContents = (index) => {
|
||||
const contents = parseVmTemplateContents({
|
||||
networks: roleConfigData?.NETWORKS?.[index],
|
||||
networks:
|
||||
roleConfigData?.NETWORKS?.[index] ||
|
||||
roleConfigData?.NETWORKDEFS?.[index],
|
||||
rdpConfig: roleConfigData?.RDP?.[index],
|
||||
remainingContent: roleConfigData?.vm_template_contents?.[index],
|
||||
schedActions: extraData?.SCHED_ACTION,
|
||||
})
|
||||
|
||||
@ -274,7 +280,16 @@ const Steps = createSteps([General, Extra, RoleDefinition, RoleConfig], {
|
||||
custom_attrs: getCustomAttributes(),
|
||||
}
|
||||
|
||||
const cleanedTemplate = convertKeysToCase(formatTemplate)
|
||||
const cleanedTemplate = {
|
||||
...convertKeysToCase(formatTemplate, true, 1),
|
||||
...(formatTemplate?.roles || formatTemplate?.ROLES
|
||||
? {
|
||||
roles: convertKeysToCase(
|
||||
formatTemplate?.roles || formatTemplate?.ROLES
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
return cleanedTemplate
|
||||
} catch (error) {}
|
||||
|
@ -67,7 +67,12 @@ const Steps = createSteps([General, UserInputs, Network, Charter], {
|
||||
} = formData
|
||||
|
||||
const formatTemplate = {
|
||||
custom_attrs_values: { ...userInputsData },
|
||||
custom_attrs_values: Object.fromEntries(
|
||||
Object.entries(userInputsData).map(([key, value]) => [
|
||||
key.toUpperCase(),
|
||||
String(value),
|
||||
])
|
||||
),
|
||||
networks_values: networkData?.NETWORKS?.map((network) => ({
|
||||
[network?.name]: {
|
||||
[['existing', 'reserve'].includes(network?.tableType)
|
||||
|
@ -66,11 +66,17 @@ function CreateServiceTemplate() {
|
||||
const onSubmit = async (jsonTemplate) => {
|
||||
try {
|
||||
if (!templateId) {
|
||||
const newTemplateId = await allocate({
|
||||
const {
|
||||
DOCUMENT: { ID: newTemplateId, NAME: templateName },
|
||||
} = await allocate({
|
||||
template: jsonTemplate,
|
||||
}).unwrap()
|
||||
|
||||
history.push(PATH.TEMPLATE.SERVICES.LIST)
|
||||
enqueueSuccess(T.SuccessServiceTemplateCreated, [newTemplateId, NAME])
|
||||
enqueueSuccess(T.SuccessServiceTemplateCreated, [
|
||||
newTemplateId,
|
||||
templateName,
|
||||
])
|
||||
} else {
|
||||
await update({
|
||||
id: templateId,
|
||||
|
@ -75,20 +75,28 @@ export const parseNetworkString = (network, reverse = false) => {
|
||||
*
|
||||
* @param {object | Array} obj - The object or array whose keys are to be converted.
|
||||
* @param {boolean} [toLower=true] - Whether to convert keys to lower case. If false, keys are converted to upper case.
|
||||
* @param {number} depth - Control depth of conversion
|
||||
* @returns {object | Array} - The input object or array with keys converted to the specified case.
|
||||
*/
|
||||
export const convertKeysToCase = (obj, toLower = true) => {
|
||||
export const convertKeysToCase = (obj, toLower = true, depth = Infinity) => {
|
||||
if (depth < 1) return obj
|
||||
|
||||
if (_.isArray(obj)) {
|
||||
return obj.map((item) => convertKeysToCase(item, toLower))
|
||||
return obj.map((item) => convertKeysToCase(item, toLower, depth))
|
||||
}
|
||||
|
||||
if (_.isObject(obj) && !_.isDate(obj) && !_.isFunction(obj)) {
|
||||
return _.mapValues(
|
||||
_.mapKeys(obj, (_value, key) =>
|
||||
toLower ? key.toLowerCase() : key.toUpperCase()
|
||||
),
|
||||
(value) => convertKeysToCase(value, toLower)
|
||||
const convertedObj = _.mapKeys(obj, (_value, key) =>
|
||||
toLower ? key.toLowerCase() : key.toUpperCase()
|
||||
)
|
||||
|
||||
if (depth > 1) {
|
||||
return _.mapValues(convertedObj, (value) =>
|
||||
convertKeysToCase(value, toLower, depth - 1)
|
||||
)
|
||||
}
|
||||
|
||||
return convertedObj
|
||||
}
|
||||
|
||||
return obj
|
||||
|
@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import _ from 'lodash'
|
||||
|
||||
/* eslint-disable no-useless-escape */
|
||||
const formatNic = (nic, parent, rdp) => {
|
||||
@ -21,24 +22,23 @@ const formatNic = (nic, parent, rdp) => {
|
||||
return `${
|
||||
parent ? 'NIC_ALIAS' : 'NIC'
|
||||
} = [\n NAME = \"${NIC}\",\n NETWORK_ID = \"$${
|
||||
NETWORK_ID !== undefined ? NETWORK_ID.toLowerCase() : ''
|
||||
NETWORK_ID !== undefined ? NETWORK_ID : ''
|
||||
}\"${rdp ? `,\n RDP = \"YES\"` : ''}${
|
||||
parent ? `,\n PARENT = \"${parent}\"` : ''
|
||||
} ]\n`
|
||||
}
|
||||
|
||||
const formatAlias = (fNics) => {
|
||||
const formatAlias = (fNics) =>
|
||||
fNics?.map((fnic) => {
|
||||
if (fnic?.alias) {
|
||||
const parent = fNics?.find(
|
||||
(nic) => nic?.NIC_NAME === fnic?.alias?.name
|
||||
)?.NIC_ID
|
||||
if (fnic?.alias || fnic?.PARENT) {
|
||||
const parent =
|
||||
fnic?.PARENT ??
|
||||
fNics?.find((nic) => nic?.NIC_NAME === fnic?.alias?.name)?.NIC_ID
|
||||
fnic.formatNic = formatNic({ [fnic?.NIC_ID]: fnic?.NIC_NAME }, parent)
|
||||
}
|
||||
|
||||
return ''
|
||||
})
|
||||
}
|
||||
|
||||
const formatSchedActions = (schedAction) => {
|
||||
const { ACTION, TIME, DAYS, END_TYPE, END_VALUE, REPEAT, ID } = schedAction
|
||||
@ -79,15 +79,69 @@ const parseSection = (section) => {
|
||||
return { header, content }
|
||||
}
|
||||
|
||||
const extractSections = (content) => {
|
||||
const sections = []
|
||||
const regex = /(NIC|NIC_ALIAS|SCHED_ACTION)\s*=\s*\[[^\]]+\]/g
|
||||
let match
|
||||
while ((match = regex.exec(content))) {
|
||||
sections.push(match[0])
|
||||
}
|
||||
|
||||
return sections
|
||||
}
|
||||
|
||||
const extractPropertiesToArray = (content) => {
|
||||
const properties = []
|
||||
const regex = /(\w+\s*=\s*"[^"]*")/g
|
||||
let match
|
||||
while ((match = regex.exec(content))) {
|
||||
properties.push(match[1])
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
const formatInstantiate = (contents) => {
|
||||
const { vmTemplateContents, customAttrsValues } = contents
|
||||
|
||||
const formatUserInputs = Object.entries(customAttrsValues)
|
||||
?.map(([input, value]) => `${input.toLowerCase()} = "${value}"`)
|
||||
?.join('\n')
|
||||
?.concat('\n')
|
||||
const sections = extractSections(vmTemplateContents)
|
||||
.map(parseSection)
|
||||
.filter(Boolean)
|
||||
|
||||
return vmTemplateContents + formatUserInputs
|
||||
const nonNicContent = vmTemplateContents.replace(
|
||||
/(NIC|NIC_ALIAS|SCHED_ACTION)\s*=\s*\[[^\]]+\]/g,
|
||||
''
|
||||
)
|
||||
|
||||
const templateProperties = extractPropertiesToArray(nonNicContent)
|
||||
|
||||
const customProperties = Object.entries(customAttrsValues).map(
|
||||
([key, value]) => `${key.toUpperCase()} = "${value}"`
|
||||
)
|
||||
|
||||
const combinedProperties = _.uniqWith(
|
||||
[...templateProperties, ...customProperties],
|
||||
_.isEqual
|
||||
)
|
||||
|
||||
const filteredProperties = combinedProperties.filter(
|
||||
(property) => !property.includes('= ""')
|
||||
)
|
||||
|
||||
const combinedContent = [
|
||||
...sections.map(({ header, content }) => {
|
||||
const props = Object.entries(content)
|
||||
.map(([key, value]) => ` ${key.toUpperCase()} = "${value}"`)
|
||||
.join(',\n')
|
||||
|
||||
return `${header} = [\n${props} ]`
|
||||
}),
|
||||
...filteredProperties,
|
||||
]
|
||||
|
||||
const formattedTemplate = combinedContent.join('\n') + '\n'
|
||||
|
||||
return formattedTemplate
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,8 +169,11 @@ const formatVmTemplateContents = (
|
||||
const sections = contents.match(
|
||||
/(NIC_ALIAS|NIC|SCHED_ACTION)\s*=\s*\[[^\]]+\]/g
|
||||
)
|
||||
const remainingContent = contents
|
||||
.replace(/(NIC_ALIAS|NIC|SCHED_ACTION)\s*=\s*\[[^\]]+\]/g, '')
|
||||
.trim()
|
||||
|
||||
if (!sections) return { networks: nics, schedActions }
|
||||
if (!sections) return { networks: nics, schedActions, remainingContent }
|
||||
|
||||
sections.forEach((section) => {
|
||||
const parsedSection = parseSection(section)
|
||||
@ -130,37 +187,52 @@ const formatVmTemplateContents = (
|
||||
}
|
||||
})
|
||||
|
||||
return { networks: nics, schedActions }
|
||||
return { networks: nics, schedActions, remainingContent }
|
||||
} else {
|
||||
const { networks, rdpConfig, schedActions } = contents
|
||||
if (!networks) {
|
||||
return ''
|
||||
}
|
||||
const { networks, rdpConfig, schedActions, remainingContent } = contents
|
||||
const preformattedNetworks = networks.every(
|
||||
(network) =>
|
||||
network?.NAME &&
|
||||
network?.NETWORK_ID &&
|
||||
network?.NAME?.replace(/_/g, '')?.startsWith('NIC') &&
|
||||
network?.NETWORK_ID?.includes('$')
|
||||
)
|
||||
|
||||
const formattedActions = schedActions?.map((action, index) =>
|
||||
formatSchedActions({ ...action, ID: index })
|
||||
)
|
||||
|
||||
const formattedNics = networks
|
||||
?.filter((net) => net?.rowSelected)
|
||||
?.filter((net) => net?.rowSelected || preformattedNetworks)
|
||||
?.map((nic, index) => ({
|
||||
formatNic: formatNic(
|
||||
{
|
||||
[`_NIC${index}`]: nic?.name,
|
||||
[`_${
|
||||
preformattedNetworks
|
||||
? nic?.NAME?.replace(/_/g, '')
|
||||
: `NIC${index}`
|
||||
}`]: preformattedNetworks
|
||||
? nic?.NETWORK_ID?.replace(/\$/g, '')
|
||||
: nic?.name,
|
||||
},
|
||||
false,
|
||||
nic?.name === rdpConfig
|
||||
preformattedNetworks ? nic?.RDP === 'YES' : nic?.name === rdpConfig
|
||||
),
|
||||
NIC_ID: `_NIC${index}`,
|
||||
NIC_NAME: nic?.name,
|
||||
NIC_NAME: preformattedNetworks
|
||||
? nic?.NETWORK_ID?.replace(/\$/g, '')
|
||||
: nic?.name,
|
||||
...(nic?.aliasIdx !== -1 && { alias: networks?.[nic?.aliasIdx] }),
|
||||
...(preformattedNetworks && nic?.PARENT ? { PARENT: nic?.PARENT } : {}),
|
||||
}))
|
||||
|
||||
formatAlias(formattedNics)
|
||||
|
||||
const vmTemplateContents = formattedNics
|
||||
?.map((nic) => nic.formatNic)
|
||||
.join('')
|
||||
.concat(formattedActions?.join('') ?? '')
|
||||
let vmTemplateContents =
|
||||
formattedNics?.map((nic) => nic.formatNic).join('') ?? ''
|
||||
|
||||
vmTemplateContents += formattedActions?.join('') ?? ''
|
||||
vmTemplateContents += remainingContent ? `\n${remainingContent}` : ''
|
||||
|
||||
return vmTemplateContents
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user