/*
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 .
*/
import { observer } from "mobx-react";
import React from "react";
import styled from "styled-components";
import { TransferItemDetails } from "@src/@types/MainItem";
import { MinionPool } from "@src/@types/MinionPool";
import { INSTANCE_OSMORPHING_MINION_POOL_MAPPINGS } from "@src/components/modules/WizardModule/WizardOptions";
import WizardScripts from "@src/components/modules/WizardModule/WizardScripts";
import { ThemeProps } from "@src/components/Theme";
import Button from "@src/components/ui/Button";
import FieldInput from "@src/components/ui/FieldInput";
import LoadingButton from "@src/components/ui/LoadingButton";
import ToggleButtonBar from "@src/components/ui/ToggleButtonBar";
import KeyboardManager from "@src/utils/KeyboardManager";
import LabelDictionary from "@src/utils/LabelDictionary";
import replicaMigrationImage from "./images/replica-migration.svg";
import replicaMigrationFields from "./replicaMigrationFields";
import type { Field } from "@src/@types/Field";
import type { Instance, InstanceScript } from "@src/@types/Instance";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding: 0 32px 32px 32px;
min-height: 0;
`;
const Image = styled.div`
${ThemeProps.exactWidth("288px")}
${ThemeProps.exactHeight("96px")}
background: url('${replicaMigrationImage}') center no-repeat;
margin: 80px 0;
`;
const OptionsBody = styled.div`
display: flex;
flex-direction: column;
`;
const ScriptsBody = styled.div`
display: flex;
flex-direction: column;
width: 100%;
overflow: auto;
min-height: 0;
margin-bottom: 32px;
`;
const Form = styled.div`
display: flex;
flex-wrap: wrap;
margin-left: -64px;
justify-content: space-between;
margin: 0 auto;
`;
const Buttons = styled.div`
display: flex;
justify-content: space-between;
width: 100%;
`;
const FieldInputStyled = styled(FieldInput)`
width: 224px;
justify-content: space-between;
margin-bottom: 32px;
`;
type Props = {
instances: Instance[];
transferItem: TransferItemDetails | null;
minionPools: MinionPool[];
loadingInstances: boolean;
defaultSkipOsMorphing?: boolean | null;
migrating?: boolean;
onCancelClick: () => void;
onMigrateClick: (opts: {
fields: Field[];
uploadedUserScripts: InstanceScript[];
removedUserScripts: InstanceScript[];
minionPoolMappings: { [instance: string]: string };
}) => void;
onResizeUpdate?: (scrollableRef: HTMLElement, scrollOffset?: number) => void;
};
type State = {
fields: Field[];
selectedBarButton: string;
uploadedScripts: InstanceScript[];
removedScripts: InstanceScript[];
minionPoolMappings: { [instance: string]: string };
};
@observer
class ReplicaMigrationOptions extends React.Component {
state: State = {
fields: [],
selectedBarButton: "options",
uploadedScripts: [],
removedScripts: [],
minionPoolMappings: {},
};
scrollableRef!: HTMLElement;
UNSAFE_componentWillMount() {
const mappings =
this.props.transferItem?.instance_osmorphing_minion_pool_mappings || {};
this.setState({
fields: replicaMigrationFields.map(f =>
f.name === "skip_os_morphing"
? { ...f, value: this.props.defaultSkipOsMorphing || null }
: f
),
minionPoolMappings: { ...mappings },
});
}
componentDidMount() {
KeyboardManager.onEnter(
"migration-options",
() => {
this.migrate();
},
2
);
}
componentDidUpdate(_: Props, prevState: State) {
if (prevState.selectedBarButton !== this.state.selectedBarButton) {
if (this.props.onResizeUpdate) {
this.props.onResizeUpdate(this.scrollableRef, 0);
}
}
}
componentWillUnmount() {
KeyboardManager.removeKeyDown("migration-options");
}
migrate() {
this.props.onMigrateClick({
fields: this.state.fields,
uploadedUserScripts: this.state.uploadedScripts,
removedUserScripts: this.state.removedScripts,
minionPoolMappings: this.state.minionPoolMappings,
});
}
handleValueChange(field: Field, value: boolean) {
this.setState(prevState => {
const fields = prevState.fields.map(f => {
const newField = { ...f };
if (f.name === field.name) {
newField.value = value;
}
return newField;
});
return { fields };
});
}
handleCancelScript(global: string | null, instanceName: string | null) {
this.setState(prevState => ({
uploadedScripts: prevState.uploadedScripts.filter(s =>
global ? s.global !== global : s.instanceId !== instanceName
),
}));
}
handleScriptUpload(script: InstanceScript) {
this.setState(prevState => ({
uploadedScripts: [...prevState.uploadedScripts, script],
}));
}
handleScriptRemove(script: InstanceScript) {
this.setState(prevState => ({
removedScripts: [...prevState.removedScripts, script],
}));
}
renderField(field: Field) {
return (
this.handleValueChange(field, value)}
description={LabelDictionary.getDescription(field.name)}
/>
);
}
renderMinionPoolMappings() {
const minionPools = this.props.minionPools.filter(
m => m.endpoint_id === this.props.transferItem?.destination_endpoint_id
);
if (!minionPools.length) {
return null;
}
const properties: Field[] = this.props.instances.map(instance => ({
name: instance.instance_name || instance.id,
label: instance.name,
type: "string",
enum: minionPools.map(minionPool => ({
name: minionPool.name,
id: minionPool.id,
})),
}));
return (
this.state.minionPoolMappings &&
this.state.minionPoolMappings[field.name]
}
layout="page"
label="Instance OSMorphing Minion Pool Mappings"
onChange={(value, field) =>
this.setState(prevState => {
const minionPoolMappings = { ...prevState.minionPoolMappings };
minionPoolMappings[field!.name] = value;
return { minionPoolMappings };
})
}
properties={properties}
labelRenderer={(propName: string) =>
propName.indexOf("/") > -1
? propName.split("/")[propName.split("/").length - 1]
: propName
}
/>
);
}
renderOptions() {
return (
<>
{this.renderMinionPoolMappings()}
>
);
}
renderScripts() {
return (
{
this.handleScriptUpload(s);
}}
onScriptDataRemove={s => {
this.handleScriptRemove(s);
}}
onCancelScript={(g, i) => {
this.handleCancelScript(g, i);
}}
uploadedScripts={this.state.uploadedScripts}
removedScripts={this.state.removedScripts}
userScriptData={this.props.transferItem?.user_scripts}
scrollableRef={(r: HTMLElement) => {
this.scrollableRef = r;
}}
layout="modal"
/>
);
}
renderBody() {
const Body =
this.state.selectedBarButton === "options" ? OptionsBody : ScriptsBody;
return (
{
this.setState({ selectedBarButton: item.value });
}}
style={{ marginBottom: "32px" }}
/>
{this.state.selectedBarButton === "options"
? this.renderOptions()
: this.renderScripts()}
);
}
render() {
return (
{this.renderBody()}
{this.props.migrating ? (
Migrating ...
) : (
)}
);
}
}
export default ReplicaMigrationOptions;