| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- Copyright (C) 2017 Cloudbase Solutions SRL
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- import Utils from "@src/utils/ObjectUtils";
- import { Field, isEnumSeparator } from "@src/@types/Field";
- import type { OptionValues, StorageMap } from "@src/@types/Endpoint";
- import type { SchemaProperties, SchemaDefinitions } from "@src/@types/Schema";
- import type { NetworkMap } from "@src/@types/Network";
- import type { InstanceScript } from "@src/@types/Instance";
- import { executionOptions } from "@src/constants";
- import { UserScriptData } from "@src/@types/MainItem";
- import { defaultSchemaToFields } from "./ConnectionSchemaPlugin";
- const migrationImageOsTypes = ["windows", "linux"];
- export const defaultFillFieldValues = (field: Field, option: OptionValues) => {
- if (field.type === "string") {
- field.enum = [...option.values];
- if (option.config_default) {
- field.default =
- typeof option.config_default === "string"
- ? option.config_default
- : option.config_default.id;
- }
- }
- if (field.type === "array") {
- field.enum = [...option.values];
- }
- if (field.type === "boolean" && option.config_default != null) {
- field.default =
- typeof option.config_default === "boolean"
- ? option.config_default
- : option.config_default === "true";
- }
- if (
- (field.type === "integer" || field.type === "number") &&
- option.config_default != null
- ) {
- field.default = Number(option.config_default);
- }
- };
- export const removeExportImageFieldValues = (field: Field) => {
- if (field.name === "export_image") {
- field.enum = field.enum?.filter(exportImageValue => {
- if (
- typeof exportImageValue === "string" ||
- isEnumSeparator(exportImageValue)
- ) {
- return true;
- }
- const isLinux =
- exportImageValue.os_type === "linux" ||
- exportImageValue.os_type === "unknown";
- if (!isLinux) {
- field.warning = "Only Linux images are listed.";
- }
- return isLinux;
- });
- }
- };
- export const defaultFillMigrationImageMapValues = (opts: {
- field: Field;
- option: OptionValues;
- migrationImageMapFieldName: string;
- requiresWindowsImage: boolean;
- }): boolean => {
- const { field, option, migrationImageMapFieldName, requiresWindowsImage } =
- opts;
- if (field.name !== migrationImageMapFieldName) {
- return false;
- }
- field.properties = migrationImageOsTypes.map(os => {
- const values = (option.values as any)
- .filter(
- (v: { os_type: string }) => v.os_type === os || v.os_type === "unknown",
- )
- .sort((v1: { os_type: string }, v2: { os_type: string }) => {
- if (v1.os_type === "unknown" && v2.os_type !== "unknown") {
- return 1;
- }
- if (v1.os_type !== "unknown" && v2.os_type === "unknown") {
- return -1;
- }
- return 0;
- });
- const unknownIndex = values.findIndex(
- (v: { os_type: string }) => v.os_type === "unknown",
- );
- if (
- unknownIndex > -1 &&
- values.filter((v: { os_type: string }) => v.os_type === "unknown")
- .length < values.length
- ) {
- values.splice(unknownIndex, 0, { separator: true });
- }
- let defaultValue = null;
- if (
- option?.config_default &&
- Object.prototype.hasOwnProperty.call(option.config_default, os)
- ) {
- // @ts-ignore
- defaultValue = option.config_default[os];
- }
- return {
- name: os,
- type: "string",
- enum: values,
- default: defaultValue,
- required: os === "linux" || (requiresWindowsImage && os === "windows"),
- };
- });
- return true;
- };
- export const defaultGetDestinationEnv = (
- options?: { [prop: string]: any } | null,
- oldOptions?: { [prop: string]: any } | null,
- ): any => {
- const env: any = {};
- const specialOptions = ["separate_vm", "title", "minion_pool_id"]
- .concat(executionOptions.map(o => o.name))
- .concat(migrationImageOsTypes);
- if (!options) {
- return env;
- }
- Object.keys(options).forEach(optionName => {
- const value = options[optionName];
- const oldValue = oldOptions?.[optionName];
- const noValue =
- (value == null || value === "") && (oldValue == null || oldValue === "");
- if (specialOptions.find(o => o === optionName) || noValue) {
- return;
- }
- if (Array.isArray(value)) {
- env[optionName] = value;
- } else if (typeof value === "object" && value != null) {
- const oldValue = oldOptions?.[optionName] || {};
- const mergedValue: any = { ...oldValue, ...value };
- const newValue: any = {};
- Object.keys(mergedValue).forEach(k => {
- if (mergedValue[k] !== null) {
- newValue[k] = mergedValue[k];
- }
- });
- env[optionName] = newValue;
- } else {
- env[optionName] = options ? Utils.trim(optionName, value) : null;
- }
- });
- return env;
- };
- export const defaultGetMigrationImageMap = (
- options: { [prop: string]: any } | null | undefined,
- oldOptions: any,
- migrationImageMapFieldName: string,
- ) => {
- console.log("defaultGetMigrationImageMap called with old env: ");
- console.log(oldOptions);
- const env: any = {};
- const usableOptions = options;
- if (!usableOptions) {
- return env;
- }
- const hasMigrationMap = Object.keys(usableOptions).find(
- k => k === migrationImageMapFieldName,
- );
- if (!hasMigrationMap) {
- return env;
- }
- migrationImageOsTypes.forEach(os => {
- let value = usableOptions[migrationImageMapFieldName][os];
- // Make sure the migr. image mapping has all the OSes filled,
- // even if only one OS mapping was updated,
- // ie. don't send just the updated OS map to the server, send them all if one was updated.
- if (!value) {
- value = oldOptions?.[migrationImageMapFieldName]?.[os];
- if (!value) {
- return;
- }
- }
- if (!env[migrationImageMapFieldName]) {
- env[migrationImageMapFieldName] = {};
- }
- env[migrationImageMapFieldName][os] = value;
- });
- return env;
- };
- export default class OptionsSchemaParserBase {
- migrationImageMapFieldName = "migr_image_map";
- parseSchemaToFields(opts: {
- schema: SchemaProperties;
- schemaDefinitions?: SchemaDefinitions | null;
- dictionaryKey?: string;
- requiresWindowsImage?: boolean;
- }) {
- const { schema, schemaDefinitions, dictionaryKey } = opts;
- return defaultSchemaToFields(schema, schemaDefinitions, dictionaryKey);
- }
- 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);
- });
- }
- fillFieldValues(opts: {
- field: Field;
- options: OptionValues[];
- requiresWindowsImage: boolean;
- customFieldName?: string;
- }) {
- const { field, options, requiresWindowsImage, customFieldName } = opts;
- const option = options.find(f =>
- customFieldName ? f.name === customFieldName : f.name === field.name,
- );
- if (!option) {
- return;
- }
- if (
- !defaultFillMigrationImageMapValues({
- field,
- option,
- migrationImageMapFieldName: this.migrationImageMapFieldName,
- requiresWindowsImage,
- })
- ) {
- defaultFillFieldValues(field, option);
- }
- }
- getDestinationEnv(
- options?: { [prop: string]: any } | null,
- oldOptions?: any,
- ) {
- const env = {
- ...defaultGetDestinationEnv(options, oldOptions),
- ...defaultGetMigrationImageMap(
- options,
- oldOptions,
- this.migrationImageMapFieldName,
- ),
- };
- return env;
- }
- getNetworkMap(networkMappings: NetworkMap[] | null | undefined) {
- const payload: any = {};
- if (!networkMappings?.length) {
- return payload;
- }
- const hasSecurityGroups = Boolean(
- networkMappings.find(nm => nm.targetNetwork!.security_groups),
- );
- networkMappings.forEach(mapping => {
- let target;
- if (hasSecurityGroups) {
- target = {
- id: mapping.targetNetwork!.id,
- security_groups: mapping.targetSecurityGroups
- ? mapping.targetSecurityGroups.map(s =>
- typeof s === "string" ? s : s.id,
- )
- : [],
- };
- } else if (mapping.targetPortKey != null) {
- target = `${mapping.targetNetwork!.id}:${mapping.targetPortKey}`;
- } else {
- target = mapping.targetNetwork!.id;
- }
- payload[mapping.sourceNic.network_name] = target;
- });
- return payload;
- }
- getStorageMap(
- defaultStorage:
- | { value: string | null; busType?: string | null }
- | undefined,
- storageMap?: StorageMap[] | null,
- configDefault?: string | null,
- ) {
- if (!defaultStorage?.value && !storageMap) {
- return null;
- }
- const payload: any = {};
- if (defaultStorage?.value) {
- payload.default = defaultStorage.value;
- if (defaultStorage.busType) {
- payload.default += `:${defaultStorage.busType}`;
- }
- }
- if (!storageMap) {
- return payload;
- }
- storageMap.forEach(mapping => {
- if (mapping.target.id === null && !configDefault) {
- return;
- }
- const getDestination = () => {
- let destination =
- mapping.target.id === null ? configDefault : mapping.target.name;
- if (mapping.targetBusType) {
- destination += `:${mapping.targetBusType}`;
- }
- return destination;
- };
- if (mapping.type === "backend") {
- if (!payload.backend_mappings) {
- payload.backend_mappings = [];
- }
- payload.backend_mappings.push({
- source: mapping.source.storage_backend_identifier,
- destination: getDestination(),
- });
- } else {
- if (!payload.disk_mappings) {
- payload.disk_mappings = [];
- }
- payload.disk_mappings.push({
- disk_id: mapping.source.id.toString(),
- destination: getDestination(),
- });
- }
- });
- return payload;
- }
- getUserScripts(
- uploadedUserScripts: InstanceScript[],
- removedUserScripts: InstanceScript[],
- userScriptData: UserScriptData | null | undefined,
- ) {
- const payload: any = userScriptData || {};
- const setPayload = (
- scripts: InstanceScript[],
- scriptProp: "global" | "instanceId",
- payloadProp: "global" | "instances",
- ) => {
- if (!scripts.length) {
- return;
- }
- payload[payloadProp] = payload[payloadProp] || {};
- scripts.forEach(script => {
- const scriptValue = script[scriptProp];
- if (!scriptValue) {
- throw new Error(
- `The uploaded script structure is missing the '${scriptProp}' property`,
- );
- }
- payload[payloadProp][scriptValue] = script.scriptContent;
- });
- };
- setPayload(
- removedUserScripts.filter(s => s.global),
- "global",
- "global",
- );
- setPayload(
- removedUserScripts.filter(s => s.instanceId),
- "instanceId",
- "instances",
- );
- setPayload(
- uploadedUserScripts.filter(s => s.global),
- "global",
- "global",
- );
- setPayload(
- uploadedUserScripts.filter(s => s.instanceId),
- "instanceId",
- "instances",
- );
- return payload;
- }
- }
|