Przeglądaj źródła

Add 'Use OVM Exporter' field support

Hide all the fields in Source Options when choosing to use OVM Exporter.
Sergiu Miclea 5 lat temu
rodzic
commit
0461ffd277

+ 1 - 1
src/@types/Field.ts

@@ -53,7 +53,7 @@ export type Field = {
   title?: string,
   title?: string,
   description?: string,
   description?: string,
   subFields?: Field[],
   subFields?: Field[],
-  groupName?: string,
+  groupName?: string
 }
 }
 
 
 const migrationImageOsTypes = ['windows', 'linux']
 const migrationImageOsTypes = ['windows', 'linux']

+ 2 - 1
src/components/organisms/EditReplica/EditReplica.tsx

@@ -451,7 +451,7 @@ class EditReplica extends React.Component<Props, State> {
       data[field.name] = value
       data[field.name] = value
     }
     }
 
 
-    if (field.enum && field.subFields) {
+    if (field.subFields) {
       field.subFields.forEach(subField => {
       field.subFields.forEach(subField => {
         const subFieldKeys = Object.keys(data).filter(k => k.indexOf(subField.name) > -1)
         const subFieldKeys = Object.keys(data).filter(k => k.indexOf(subField.name) > -1)
         subFieldKeys.forEach(k => {
         subFieldKeys.forEach(k => {
@@ -488,6 +488,7 @@ class EditReplica extends React.Component<Props, State> {
       try {
       try {
         await replicaStore.update({
         await replicaStore.update({
           replica: this.props.replica as any,
           replica: this.props.replica as any,
+          sourceEndpoint: this.props.sourceEndpoint,
           destinationEndpoint: this.props.destinationEndpoint,
           destinationEndpoint: this.props.destinationEndpoint,
           updateData,
           updateData,
           defaultStorage: this.getDefaultStorage(),
           defaultStorage: this.getDefaultStorage(),

+ 17 - 7
src/components/organisms/WizardOptions/WizardOptions.tsx

@@ -214,12 +214,14 @@ class WizardOptions extends React.Component<Props> {
       const dictionaryLabel = LabelDictionary.get('separate_vm')
       const dictionaryLabel = LabelDictionary.get('separate_vm')
       const label = this.props.wizardType === 'migration' ? dictionaryLabel : dictionaryLabel.replace('Migration', 'Replica')
       const label = this.props.wizardType === 'migration' ? dictionaryLabel : dictionaryLabel.replace('Migration', 'Replica')
       fieldsSchema.push({
       fieldsSchema.push({
-        name: 'separate_vm', label, type: 'boolean', default: true,
+        name: 'separate_vm', label, type: 'boolean', default: true, nullableBoolean: false,
       })
       })
     }
     }
 
 
     if (this.props.wizardType === 'migration' || this.props.wizardType === 'migration-destination-options-edit') {
     if (this.props.wizardType === 'migration' || this.props.wizardType === 'migration-destination-options-edit') {
-      fieldsSchema.push({ name: 'skip_os_morphing', type: 'boolean', default: false })
+      fieldsSchema.push({
+        name: 'skip_os_morphing', type: 'boolean', default: false, nullableBoolean: false,
+      })
     }
     }
 
 
     if (this.props.wizardType === 'migration' || this.props.wizardType === 'replica'
     if (this.props.wizardType === 'migration' || this.props.wizardType === 'replica'
@@ -237,7 +239,9 @@ class WizardOptions extends React.Component<Props> {
     }
     }
 
 
     if (this.props.wizardType === 'replica') {
     if (this.props.wizardType === 'replica') {
-      fieldsSchema.push({ name: 'execute_now', type: 'boolean', default: true })
+      fieldsSchema.push({
+        name: 'execute_now', type: 'boolean', default: true, nullableBoolean: false,
+      })
       const executeNowValue = this.getFieldValue('execute_now', true)
       const executeNowValue = this.getFieldValue('execute_now', true)
       fieldsSchema.push({
       fieldsSchema.push({
         name: 'execute_now_options',
         name: 'execute_now_options',
@@ -386,7 +390,6 @@ class WizardOptions extends React.Component<Props> {
     }
     }
 
 
     let fieldsSchema: Field[] = this.getDefaultSimpleFieldsSchema()
     let fieldsSchema: Field[] = this.getDefaultSimpleFieldsSchema()
-    const nonNullableBooleans: string[] = fieldsSchema.filter(f => f.type === 'boolean').map(f => f.name)
 
 
     fieldsSchema = fieldsSchema.concat(this.props.fields.filter(f => f.required))
     fieldsSchema = fieldsSchema.concat(this.props.fields.filter(f => f.required))
 
 
@@ -395,18 +398,25 @@ class WizardOptions extends React.Component<Props> {
       fieldsSchema = fieldsSchema.concat(this.props.fields.filter(f => !f.required))
       fieldsSchema = fieldsSchema.concat(this.props.fields.filter(f => !f.required))
     }
     }
 
 
+    const nonNullableBooleans: string[] = fieldsSchema.filter(f => f.type === 'boolean' && f.nullableBoolean === false).map(f => f.name)
+
     // Add subfields for enums which have them
     // Add subfields for enums which have them
     let subFields: any[] = []
     let subFields: any[] = []
     fieldsSchema.forEach(f => {
     fieldsSchema.forEach(f => {
-      if (!f.enum || !f.subFields) {
+      if (!f.subFields) {
         return
         return
       }
       }
       const value = this.getFieldValue(f.name, f.default)
       const value = this.getFieldValue(f.name, f.default)
       if (!f.subFields) {
       if (!f.subFields) {
         return
         return
       }
       }
-      const subField = f.subFields.find(sf => sf.name === `${String(value)}_options`)
-      if (subField && subField.properties) {
+      let subField: Field | undefined
+      if (f.type === 'boolean') {
+        subField = value ? f.subFields[1] : f.subFields[0]
+      } else {
+        subField = f.subFields.find(sf => sf.name === `${String(value)}_options`)
+      }
+      if (subField?.properties) {
         subFields = [...subFields, ...subField.properties]
         subFields = [...subFields, ...subField.properties]
       }
       }
     })
     })

+ 2 - 0
src/constants.ts

@@ -66,6 +66,7 @@ export const executionOptions = [
     name: 'shutdown_instances',
     name: 'shutdown_instances',
     type: 'boolean',
     type: 'boolean',
     defaultValue: false,
     defaultValue: false,
+    nullableBoolean: false,
   },
   },
 ]
 ]
 
 
@@ -74,6 +75,7 @@ export const migrationFields = [
     name: 'shutdown_instances',
     name: 'shutdown_instances',
     type: 'boolean',
     type: 'boolean',
     default: false,
     default: false,
+    nullableBoolean: false,
     description: 'Whether or not Coriolis should power off the source VM before performing the final incremental sync. This guarantees consistency of the exported VM\'s filesystems, but implies downtime for the source VM during the final sync.',
     description: 'Whether or not Coriolis should power off the source VM before performing the final incremental sync. This guarantees consistency of the exported VM\'s filesystems, but implies downtime for the source VM during the final sync.',
   },
   },
   {
   {

+ 14 - 0
src/plugins/endpoint/default/OptionsSchemaPlugin.ts

@@ -161,6 +161,20 @@ export default class OptionsSchemaParser {
     return defaultSchemaToFields(schema, schemaDefinitions, dictionaryKey)
     return defaultSchemaToFields(schema, schemaDefinitions, dictionaryKey)
   }
   }
 
 
+  static sortFields(fields: Field[]) {
+    fields.sort((a, b) => {
+      if (a.required && !b.required) {
+        return -1
+      }
+
+      if (!a.required && b.required) {
+        return 1
+      }
+
+      return a.name.localeCompare(b.name)
+    })
+  }
+
   static fillFieldValues(field: Field, options: OptionValues[], customFieldName?: string) {
   static fillFieldValues(field: Field, options: OptionValues[], customFieldName?: string) {
     const option = options
     const option = options
       .find(f => (customFieldName ? f.name === customFieldName : f.name === field.name))
       .find(f => (customFieldName ? f.name === customFieldName : f.name === field.name))

+ 20 - 16
src/plugins/endpoint/openstack/OptionsSchemaPlugin.ts

@@ -34,27 +34,31 @@ export default class OptionsSchemaParser {
     schemaDefinitions: SchemaDefinitions | null | undefined,
     schemaDefinitions: SchemaDefinitions | null | undefined,
     dictionaryKey: string,
     dictionaryKey: string,
   ) {
   ) {
-    const fields = DefaultOptionsSchemaPlugin
-      .parseSchemaToFields(schema, schemaDefinitions, dictionaryKey)
+    const fields = DefaultOptionsSchemaPlugin.parseSchemaToFields(schema, schemaDefinitions, dictionaryKey)
     const exportMechField = fields.find(f => f.name === 'replica_export_mechanism')
     const exportMechField = fields.find(f => f.name === 'replica_export_mechanism')
-    if (exportMechField) {
-      exportMechField.subFields = []
-      exportMechField.enum.forEach((exportType: any) => {
-        const exportTypeFieldIdx = fields.findIndex(f => f.name === `${exportType}_options`)
-        if (exportTypeFieldIdx > -1) {
-          const subField = fields[exportTypeFieldIdx]
-          if (subField.properties && subField.properties.length) {
-            subField.properties = subField.properties
-              .map((p: any) => ({ ...p, groupName: subField.name }))
-          }
-          exportMechField.subFields.push(subField)
-          fields.splice(exportTypeFieldIdx, 1)
-        }
-      })
+    if (!exportMechField) {
+      return fields
     }
     }
+    exportMechField.subFields = []
+    exportMechField.enum.forEach((exportType: any) => {
+      const exportTypeFieldIdx = fields.findIndex(f => f.name === `${exportType}_options`)
+      if (exportTypeFieldIdx === -1) {
+        return
+      }
+      const subField = fields[exportTypeFieldIdx]
+      if (subField.properties?.length) {
+        subField.properties = subField.properties.map((p: Field) => ({ ...p, groupName: subField.name }))
+      }
+      exportMechField.subFields.push(subField)
+      fields.splice(exportTypeFieldIdx, 1)
+    })
     return fields
     return fields
   }
   }
 
 
+  static sortFields(fields: Field[]) {
+    DefaultOptionsSchemaPlugin.sortFields(fields)
+  }
+
   static fillFieldValues(field: Field, options: OptionValues[]) {
   static fillFieldValues(field: Field, options: OptionValues[]) {
     if (field.name === 'replica_export_mechanism' && field.subFields) {
     if (field.name === 'replica_export_mechanism' && field.subFields) {
       field.subFields.forEach(sf => {
       field.subFields.forEach(sf => {

+ 58 - 14
src/plugins/endpoint/ovm/OptionsSchemaPlugin.ts

@@ -34,8 +34,31 @@ export default class OptionsSchemaParser {
     schemaDefinitions: SchemaDefinitions | null | undefined,
     schemaDefinitions: SchemaDefinitions | null | undefined,
     dictionaryKey: string,
     dictionaryKey: string,
   ) {
   ) {
-    const fields = DefaultOptionsSchemaPlugin
-      .parseSchemaToFields(schema, schemaDefinitions, dictionaryKey)
+    const fields: Field[] = DefaultOptionsSchemaPlugin.parseSchemaToFields(schema, schemaDefinitions, dictionaryKey)
+    const useCoriolisExporterField = fields.find(f => f.name === 'use_coriolis_exporter')
+    if (useCoriolisExporterField) {
+      const usableFields: Field[] = [
+        {
+          ...useCoriolisExporterField,
+          nullableBoolean: false,
+          default: false,
+          subFields: [
+            {
+              name: 'generic_exporter_options',
+              type: 'object',
+              properties: fields.filter(f => f.name !== 'use_coriolis_exporter')
+                .map(f => ({ ...f, groupName: 'generic_exporter_options' })),
+            },
+            {
+              name: 'ovm_exporter_options',
+              type: 'object',
+              properties: [],
+            },
+          ],
+        },
+      ]
+      return usableFields
+    }
     fields.forEach(f => {
     fields.forEach(f => {
       if (
       if (
         f.name !== 'migr_template_username_map'
         f.name !== 'migr_template_username_map'
@@ -63,25 +86,46 @@ export default class OptionsSchemaParser {
     return fields
     return fields
   }
   }
 
 
+  static sortFields(fields: Field[]) {
+    DefaultOptionsSchemaPlugin.sortFields(fields)
+  }
+
   static fillFieldValues(field: Field, options: OptionValues[]) {
   static fillFieldValues(field: Field, options: OptionValues[]) {
-    const option = options.find(f => f.name === field.name)
-    if (!option) {
-      return
-    }
-    if (!defaultFillMigrationImageMapValues(
-      field,
-      option,
-      this.migrationImageMapFieldName,
-    )) {
-      defaultFillFieldValues(field, option)
+    if (field.name === 'use_coriolis_exporter') {
+      field.subFields?.forEach(sf => {
+        if (sf.properties) {
+          sf.properties.forEach(f => {
+            DefaultOptionsSchemaPlugin.fillFieldValues(f, options, f.name.split('/')[1])
+          })
+        }
+      })
+    } else {
+      const option = options.find(f => f.name === field.name)
+      if (!option) {
+        return
+      }
+      if (!defaultFillMigrationImageMapValues(
+        field,
+        option,
+        this.migrationImageMapFieldName,
+      )) {
+        defaultFillFieldValues(field, option)
+      }
     }
     }
   }
   }
 
 
   static getDestinationEnv(options: { [prop: string]: any } | null, oldOptions?: any) {
   static getDestinationEnv(options: { [prop: string]: any } | null, oldOptions?: any) {
+    let newOptions: any = { ...options }
+    if (newOptions.use_coriolis_exporter != null) {
+      newOptions = { use_coriolis_exporter: newOptions.use_coriolis_exporter }
+    }
+    if (options?.generic_exporter_options) {
+      newOptions = { ...options.generic_exporter_options, use_coriolis_exporter: false }
+    }
     const env = {
     const env = {
-      ...defaultGetDestinationEnv(options, oldOptions),
+      ...defaultGetDestinationEnv(newOptions, oldOptions),
       ...defaultGetMigrationImageMap(
       ...defaultGetMigrationImageMap(
-        options,
+        newOptions,
         oldOptions,
         oldOptions,
         this.migrationImageMapFieldName,
         this.migrationImageMapFieldName,
       ),
       ),

+ 8 - 5
src/sources/ReplicaSource.ts

@@ -212,6 +212,7 @@ class ReplicaSource {
 
 
   async update(options: {
   async update(options: {
     replica: ReplicaItemDetails,
     replica: ReplicaItemDetails,
+    sourceEndpoint: Endpoint,
     destinationEndpoint: Endpoint,
     destinationEndpoint: Endpoint,
     updateData: UpdateData,
     updateData: UpdateData,
     defaultStorage: string | null | undefined,
     defaultStorage: string | null | undefined,
@@ -223,9 +224,11 @@ class ReplicaSource {
       updateData,
       updateData,
       defaultStorage,
       defaultStorage,
       storageConfigDefault,
       storageConfigDefault,
+      sourceEndpoint,
     } = options
     } = options
 
 
-    const parser = OptionsSchemaPlugin.for(destinationEndpoint.type)
+    const sourceParser = OptionsSchemaPlugin.for(sourceEndpoint.type)
+    const destinationParser = OptionsSchemaPlugin.for(destinationEndpoint.type)
     const payload: any = { replica: {} }
     const payload: any = { replica: {} }
 
 
     if (updateData.destination.title) {
     if (updateData.destination.title) {
@@ -233,10 +236,10 @@ class ReplicaSource {
     }
     }
 
 
     if (updateData.network.length > 0) {
     if (updateData.network.length > 0) {
-      payload.replica.network_map = parser.getNetworkMap(updateData.network)
+      payload.replica.network_map = destinationParser.getNetworkMap(updateData.network)
     }
     }
     if (Object.keys(updateData.source).length > 0) {
     if (Object.keys(updateData.source).length > 0) {
-      const sourceEnv = parser.getDestinationEnv(updateData.source, replica.source_environment)
+      const sourceEnv = sourceParser.getDestinationEnv(updateData.source, replica.source_environment)
       if (updateData.source.minion_pool_id !== undefined) {
       if (updateData.source.minion_pool_id !== undefined) {
         payload.replica.origin_minion_pool_id = updateData.source.minion_pool_id
         payload.replica.origin_minion_pool_id = updateData.source.minion_pool_id
       }
       }
@@ -246,7 +249,7 @@ class ReplicaSource {
     }
     }
 
 
     if (Object.keys(updateData.destination).length > 0) {
     if (Object.keys(updateData.destination).length > 0) {
-      const destEnv = parser.getDestinationEnv(updateData.destination,
+      const destEnv = destinationParser.getDestinationEnv(updateData.destination,
         { ...replica, ...replica.destination_environment })
         { ...replica, ...replica.destination_environment })
 
 
       const newMinionMappings = destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS]
       const newMinionMappings = destEnv[INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS]
@@ -266,7 +269,7 @@ class ReplicaSource {
     }
     }
 
 
     if (defaultStorage || updateData.storage.length > 0) {
     if (defaultStorage || updateData.storage.length > 0) {
-      payload.replica.storage_mappings = parser
+      payload.replica.storage_mappings = destinationParser
         .getStorageMap(defaultStorage, updateData.storage, storageConfigDefault)
         .getStorageMap(defaultStorage, updateData.storage, storageConfigDefault)
     }
     }
 
 

+ 1 - 11
src/sources/Schemas.ts

@@ -35,17 +35,7 @@ class SchemaParser {
     const parser = OptionsSchemaPlugin.for(provider)
     const parser = OptionsSchemaPlugin.for(provider)
     const schemaRoot = schema.oneOf ? schema.oneOf[0] : schema
     const schemaRoot = schema.oneOf ? schema.oneOf[0] : schema
     const fields = parser.parseSchemaToFields(schemaRoot, schema.definitions, dictionaryKey)
     const fields = parser.parseSchemaToFields(schemaRoot, schema.definitions, dictionaryKey)
-    fields.sort((a, b) => {
-      if (a.required && !b.required) {
-        return -1
-      }
-
-      if (!a.required && b.required) {
-        return 1
-      }
-
-      return a.name.localeCompare(b.name)
-    })
+    parser.sortFields(fields)
     return fields
     return fields
   }
   }
 
 

+ 1 - 0
src/stores/ReplicaStore.ts

@@ -233,6 +233,7 @@ class ReplicaStore {
 
 
   async update(options: {
   async update(options: {
     replica: ReplicaItemDetails,
     replica: ReplicaItemDetails,
+    sourceEndpoint: Endpoint,
     destinationEndpoint: Endpoint,
     destinationEndpoint: Endpoint,
     updateData: UpdateData,
     updateData: UpdateData,
     defaultStorage: string | null | undefined,
     defaultStorage: string | null | undefined,

+ 1 - 1
src/stores/WizardStore.ts

@@ -51,7 +51,7 @@ const updateOptions = (
     options[data.field.name] = data.value
     options[data.field.name] = data.value
   }
   }
 
 
-  if (data.field.enum && data.field.subFields) {
+  if (data.field.subFields) {
     data.field.subFields.forEach(subField => {
     data.field.subFields.forEach(subField => {
       const subFieldKeys = Object.keys(options).filter(k => k.indexOf(subField.name) > -1)
       const subFieldKeys = Object.keys(options).filter(k => k.indexOf(subField.name) > -1)
       subFieldKeys.forEach(k => {
       subFieldKeys.forEach(k => {