-
Tenant Size
+
Capacity
Please select the desired capacity
@@ -376,11 +267,6 @@ const TenantSize = ({
{distribution.error}
)}
- {resourcesSize.error !== "" && (
-
- {resourcesSize.error}
-
- )}
-
- {
- if (e.target.validity.valid) {
- updateField("cpuToUse", e.target.value);
- }
- }}
- value={cpuToUse}
- disabled={selectedStorageClass === ""}
- min="1"
- max={maxCPUsUse}
- error={
- parseInt(cpuToUse) > parseInt(maxCPUsUse) ||
- parseInt(cpuToUse) <= 0 ||
- isNaN(parseInt(cpuToUse))
- ? "Invalid CPU Configuration"
- : ""
- }
- pattern={"[0-9]*"}
- />
-
-
-
- ) => {
- updateField("memoryNode", e.target.value);
- cleanValidation("memory_per_node");
- }}
- label="Memory per Node [Gi]"
- value={memoryNode}
- disabled={selectedStorageClass === ""}
- required
- error={validationErrors["memory_per_node"] || ""}
- min="2"
- max={maxMemorySize}
- />
-
+
+
);
};
const mapState = (state: AppState) => ({
- advancedMode: state.tenants.createTenant.advancedModeOn,
volumeSize: state.tenants.createTenant.fields.tenantSize.volumeSize,
sizeFactor: state.tenants.createTenant.fields.tenantSize.sizeFactor,
drivesPerServer: state.tenants.createTenant.fields.tenantSize.drivesPerServer,
@@ -530,19 +374,13 @@ const mapState = (state: AppState) => ({
ecParity: state.tenants.createTenant.fields.tenantSize.ecParity,
ecParityChoices: state.tenants.createTenant.fields.tenantSize.ecParityChoices,
cleanECChoices: state.tenants.createTenant.fields.tenantSize.cleanECChoices,
- maxAllocableMemo:
- state.tenants.createTenant.fields.tenantSize.maxAllocableMemo,
+
resourcesSize: state.tenants.createTenant.fields.tenantSize.resourcesSize,
distribution: state.tenants.createTenant.fields.tenantSize.distribution,
ecParityCalc: state.tenants.createTenant.fields.tenantSize.ecParityCalc,
limitSize: state.tenants.createTenant.limitSize,
selectedStorageClass:
state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
- cpuToUse: state.tenants.createTenant.fields.tenantSize.cpuToUse,
- maxAllocatableResources:
- state.tenants.createTenant.fields.tenantSize.maxAllocatableResources,
- maxCPUsUse: state.tenants.createTenant.fields.tenantSize.maxCPUsUse,
- maxMemorySize: state.tenants.createTenant.fields.tenantSize.maxMemorySize,
});
const connector = connect(mapState, {
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeMK.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeMK.tsx
new file mode 100644
index 000000000..3d54b5b6f
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeMK.tsx
@@ -0,0 +1,385 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 MinIO, Inc.
+//
+// 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 React, { Fragment, useCallback, useEffect, useState } from "react";
+import { connect } from "react-redux";
+import { Theme } from "@mui/material/styles";
+import { SelectChangeEvent } from "@mui/material";
+import get from "lodash/get";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import { AppState } from "../../../../../../store";
+import { isPageValid, updateAddField } from "../../../actions";
+import {
+ formFieldStyles,
+ modalBasic,
+ wizardCommon,
+} from "../../../../Common/FormComponents/common/styleLibrary";
+import Grid from "@mui/material/Grid";
+import { erasureCodeCalc, getBytes } from "../../../../../../common/utils";
+import { clearValidationError } from "../../../utils";
+import { ecListTransform, Opts } from "../../../ListTenants/utils";
+import { IResourcesSize } from "../../../ListTenants/types";
+import {
+ IErasureCodeCalc,
+ IStorageDistribution,
+} from "../../../../../../common/types";
+import { commonFormValidation } from "../../../../../../utils/validationFunctions";
+import api from "../../../../../../common/api";
+import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+import SelectWrapper from "../../../../Common/FormComponents/SelectWrapper/SelectWrapper";
+import {
+ IMkEnvs,
+ IntegrationConfiguration,
+ mkPanelConfigurations,
+} from "./utils";
+
+interface ITenantSizeAWSProps {
+ classes: any;
+ updateAddField: typeof updateAddField;
+ isPageValid: typeof isPageValid;
+ volumeSize: string;
+ sizeFactor: string;
+ drivesPerServer: string;
+ nodes: string;
+ memoryNode: string;
+ ecParity: string;
+ ecParityChoices: Opts[];
+ cleanECChoices: string[];
+ resourcesSize: IResourcesSize;
+ distribution: any;
+ ecParityCalc: IErasureCodeCalc;
+ limitSize: any;
+ selectedStorageType: string;
+ cpuToUse: string;
+ maxCPUsUse: string;
+ formToRender?: IMkEnvs;
+ integrationSelection: IntegrationConfiguration;
+}
+
+const styles = (theme: Theme) =>
+ createStyles({
+ compositeFieldContainer: {
+ display: "flex",
+ alignItems: "center",
+ },
+ compositeAddOn: {
+ marginLeft: 10,
+ "& div": {
+ marginBottom: 0,
+ },
+ "@media (max-width: 900px)": {
+ "& div": {
+ marginTop: 5,
+ },
+ },
+ },
+ ...formFieldStyles,
+ ...modalBasic,
+ ...wizardCommon,
+ });
+
+const TenantSizeMK = ({
+ classes,
+ updateAddField,
+ isPageValid,
+ volumeSize,
+ sizeFactor,
+ drivesPerServer,
+ nodes,
+ memoryNode,
+ ecParity,
+ ecParityChoices,
+ cleanECChoices,
+ resourcesSize,
+ distribution,
+ ecParityCalc,
+ limitSize,
+ cpuToUse,
+ selectedStorageType,
+ maxCPUsUse,
+ formToRender,
+ integrationSelection,
+}: ITenantSizeAWSProps) => {
+ const [validationErrors, setValidationErrors] = useState
({});
+
+ // Common
+ const updateField = useCallback(
+ (field: string, value: any) => {
+ updateAddField("tenantSize", field, value);
+ },
+ [updateAddField]
+ );
+
+ const updateMainField = useCallback(
+ (field: string, value: string) => {
+ updateAddField("nameTenant", field, value);
+ },
+ [updateAddField]
+ );
+
+ const cleanValidation = (fieldName: string) => {
+ setValidationErrors(clearValidationError(validationErrors, fieldName));
+ };
+
+ /*Debounce functions*/
+
+ // Storage Quotas
+ useEffect(() => {
+ if (ecParityChoices.length > 0 && distribution.error === "") {
+ const ecCodeValidated = erasureCodeCalc(
+ cleanECChoices,
+ distribution.persistentVolumes,
+ distribution.pvSize,
+ distribution.nodes
+ );
+
+ updateField("ecParityCalc", ecCodeValidated);
+
+ if (!cleanECChoices.includes(ecParity) || ecParity === "") {
+ updateField("ecParity", ecCodeValidated.defaultEC);
+ }
+ }
+ }, [ecParity, ecParityChoices, distribution, cleanECChoices, updateField]);
+ /*End debounce functions*/
+
+ /*Set location Storage Types*/
+ useEffect(() => {
+ if (formToRender !== undefined && parseInt(nodes) >= 4) {
+ const setConfigs = mkPanelConfigurations[formToRender];
+ const keyCount = Object.keys(setConfigs).length;
+
+ //Configuration is filled
+ if (keyCount > 0) {
+ const configs: IntegrationConfiguration[] = get(
+ setConfigs,
+ "configurations",
+ []
+ );
+
+ const mainSelection = configs.find(
+ (item) => item.typeSelection === selectedStorageType
+ );
+
+ if (mainSelection) {
+ updateField("integrationSelection", mainSelection);
+ updateMainField("selectedStorageClass", mainSelection.storageClass);
+
+ let pvSize = parseInt(
+ getBytes(
+ mainSelection.driveSize.driveSize,
+ mainSelection.driveSize.sizeUnit,
+ true
+ ),
+ 10
+ );
+
+ const distrCalculate: IStorageDistribution = {
+ pvSize,
+ nodes: parseInt(nodes),
+ disks: mainSelection.drivesPerServer,
+ persistentVolumes: mainSelection.drivesPerServer * parseInt(nodes),
+ error: "",
+ };
+
+ updateField("distribution", distrCalculate);
+ // apply requests, half of the available resources
+ updateField(
+ "resourcesCPURequest",
+ Math.max(1, mainSelection.CPU / 2)
+ );
+ updateField(
+ "resourcesMemoryRequest",
+ Math.max(2, mainSelection.memory / 2)
+ );
+ }
+ }
+ }
+ }, [nodes, selectedStorageType, formToRender, updateField, updateMainField]);
+
+ /*Calculate Allocation End*/
+
+ /* Validations of pages */
+
+ useEffect(() => {
+ const commonValidation = commonFormValidation([
+ {
+ fieldKey: "nodes",
+ required: true,
+ value: nodes,
+ customValidation: parseInt(nodes) < 4,
+ customValidationMessage: "Al least 4 servers must be selected",
+ },
+ ]);
+
+ isPageValid(
+ "tenantSize",
+ !("nodes" in commonValidation) &&
+ distribution.error === "" &&
+ ecParityCalc.error === 0 &&
+ resourcesSize.error === "" &&
+ ecParity !== "" &&
+ parseInt(nodes) >= 4
+ );
+
+ setValidationErrors(commonValidation);
+ }, [
+ nodes,
+ volumeSize,
+ sizeFactor,
+ memoryNode,
+ distribution,
+ ecParityCalc,
+ resourcesSize,
+ limitSize,
+ selectedStorageType,
+ cpuToUse,
+ maxCPUsUse,
+ isPageValid,
+ drivesPerServer,
+ ecParity,
+ ]);
+
+ useEffect(() => {
+ if (integrationSelection.drivesPerServer !== 0) {
+ // Get EC Value
+ if (nodes.trim() !== "") {
+ api
+ .invoke(
+ "GET",
+ `api/v1/get-parity/${nodes}/${integrationSelection.drivesPerServer}`
+ )
+ .then((ecList: string[]) => {
+ updateField("ecParityChoices", ecListTransform(ecList));
+ updateField("cleanECChoices", ecList);
+ })
+ .catch((err: any) => {
+ updateField("ecparityChoices", []);
+ isPageValid("tenantSize", false);
+ updateField("ecParity", "");
+ });
+ }
+ }
+ }, [integrationSelection, nodes, isPageValid, updateField]);
+
+ /* End Validation of pages */
+
+ return (
+
+
+
+
Tenant Size
+
+ Please select the desired capacity
+
+
+
+ {distribution.error !== "" && (
+
+ {distribution.error}
+
+ )}
+ {resourcesSize.error !== "" && (
+
+ {resourcesSize.error}
+
+ )}
+
+ ) => {
+ if (e.target.validity.valid) {
+ updateField("nodes", e.target.value);
+ cleanValidation("nodes");
+ }
+ }}
+ label="Number of Servers"
+ disabled={selectedStorageType === ""}
+ value={nodes}
+ min="4"
+ required
+ error={validationErrors["nodes"] || ""}
+ pattern={"[0-9]*"}
+ />
+
+
+ ) => {
+ updateField("ecParity", e.target.value as string);
+ }}
+ label="Erasure Code Parity"
+ disabled={selectedStorageType === ""}
+ value={ecParity}
+ options={ecParityChoices}
+ />
+
+ Please select the desired parity. This setting will change the max
+ usable capacity in the cluster
+
+
+
+ );
+};
+
+const mapState = (state: AppState) => () => {
+ const createTenant = state.tenants.createTenant;
+
+ const {
+ memoryNode,
+ ecParityChoices,
+ distribution,
+ cleanECChoices,
+ sizeFactor,
+ ecParity,
+ cpuToUse,
+ integrationSelection,
+ resourcesSize,
+ drivesPerServer,
+ maxCPUsUse,
+ ecParityCalc,
+ volumeSize,
+ nodes,
+ } = createTenant.fields.tenantSize;
+
+ return {
+ volumeSize,
+ sizeFactor,
+ drivesPerServer,
+ nodes,
+ memoryNode,
+ ecParity,
+ ecParityChoices,
+ cleanECChoices,
+ resourcesSize,
+ distribution,
+ ecParityCalc,
+ cpuToUse,
+ maxCPUsUse,
+ integrationSelection,
+ limitSize: createTenant.limitSize,
+ selectedStorageType: createTenant.fields.nameTenant.selectedStorageType,
+ };
+};
+
+const connector = connect(mapState, {
+ updateAddField,
+ isPageValid,
+});
+
+export default withStyles(styles)(connector(TenantSizeMK));
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeResources.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeResources.tsx
new file mode 100644
index 000000000..24fd2ec36
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/TenantSizeResources.tsx
@@ -0,0 +1,400 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 MinIO, Inc.
+//
+// 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 React, { Fragment, useCallback, useEffect } from "react";
+import { connect } from "react-redux";
+import { Theme } from "@mui/material/styles";
+import createStyles from "@mui/styles/createStyles";
+import withStyles from "@mui/styles/withStyles";
+import get from "lodash/get";
+import { AppState } from "../../../../../../store";
+import { isPageValid, updateAddField } from "../../../actions";
+import {
+ formFieldStyles,
+ modalBasic,
+ wizardCommon,
+} from "../../../../Common/FormComponents/common/styleLibrary";
+import Grid from "@mui/material/Grid";
+import { IResourcesSize } from "../../../ListTenants/types";
+import { AllocableResourcesResponse } from "../../../types";
+import api from "../../../../../../common/api";
+import InputBoxWrapper from "../../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+import FormSwitchWrapper from "../../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+import { floor } from "lodash";
+
+interface ITenantSizeResourcesProps {
+ classes: any;
+ updateAddField: typeof updateAddField;
+ isPageValid: typeof isPageValid;
+ nodes: string;
+ resourcesSize: IResourcesSize;
+ selectedStorageClass: string;
+ maxAllocatableResources: AllocableResourcesResponse;
+ maxCPUsUse: string;
+ maxMemorySize: string;
+
+ resourcesSpecifyLimit: boolean;
+ resourcesCPURequestError: string;
+ resourcesCPURequest: string;
+ resourcesCPULimitError: string;
+ resourcesCPULimit: string;
+ resourcesMemoryRequestError: string;
+ resourcesMemoryRequest: string;
+ resourcesMemoryLimitError: string;
+ resourcesMemoryLimit: string;
+}
+
+const styles = (theme: Theme) =>
+ createStyles({
+ compositeFieldContainer: {
+ display: "flex",
+ alignItems: "center",
+ },
+ compositeAddOn: {
+ marginLeft: 10,
+ "& div": {
+ marginBottom: 0,
+ },
+ "@media (max-width: 900px)": {
+ "& div": {
+ marginTop: 5,
+ },
+ },
+ },
+ ...formFieldStyles,
+ ...modalBasic,
+ ...wizardCommon,
+ });
+
+const TenantSizeResources = ({
+ classes,
+ updateAddField,
+ isPageValid,
+ nodes,
+
+ resourcesSize,
+ selectedStorageClass,
+ maxAllocatableResources,
+ maxCPUsUse,
+ maxMemorySize,
+ resourcesSpecifyLimit,
+ resourcesCPURequestError,
+ resourcesCPURequest,
+ resourcesCPULimitError,
+ resourcesCPULimit,
+ resourcesMemoryRequestError,
+ resourcesMemoryRequest,
+ resourcesMemoryLimitError,
+ resourcesMemoryLimit,
+}: ITenantSizeResourcesProps) => {
+ // Common
+ const updateField = useCallback(
+ (field: string, value: any) => {
+ updateAddField("tenantSize", field, value);
+ },
+ [updateAddField]
+ );
+
+ /*Debounce functions*/
+
+ useEffect(() => {
+ isPageValid(
+ "tenantSize",
+ resourcesMemoryRequestError === "" &&
+ resourcesMemoryLimitError === "" &&
+ resourcesCPURequestError === "" &&
+ resourcesCPULimitError === ""
+ );
+ }, [
+ isPageValid,
+ resourcesMemoryRequestError,
+ resourcesMemoryLimitError,
+ resourcesCPURequestError,
+ resourcesCPULimitError,
+ ]);
+
+ /*End debounce functions*/
+
+ /*Calculate Allocation*/
+ useEffect(() => {
+ // Get allocatable Resources
+ api
+ .invoke("GET", `api/v1/cluster/allocatable-resources?num_nodes=${nodes}`)
+ .then((res: AllocableResourcesResponse) => {
+ updateField("maxAllocatableResources", res);
+
+ const maxAllocatableResources = res;
+
+ const memoryExists = get(
+ maxAllocatableResources,
+ "min_allocatable_mem",
+ false
+ );
+
+ const cpuExists = get(
+ maxAllocatableResources,
+ "min_allocatable_cpu",
+ false
+ );
+
+ if (memoryExists === false || cpuExists === false) {
+ updateField("cpuToUse", 0);
+
+ updateField("maxMemorySize", "");
+ updateField("maxCPUsUse", "");
+
+ return;
+ }
+
+ const maxMemory = floor(
+ res.mem_priority.max_allocatable_mem / 1024 / 1024 / 1024
+ );
+ // We default to Best CPU Configuration
+ updateField("maxMemorySize", maxMemory.toString());
+ updateField(
+ "maxCPUsUse",
+ res.cpu_priority.max_allocatable_cpu.toString()
+ );
+
+ const maxAllocatableCPU = get(
+ maxAllocatableResources,
+ "cpu_priority.max_allocatable_cpu",
+ 0
+ );
+
+ const baseCpuUse = Math.max(1, floor(maxAllocatableCPU / 2));
+ updateField("resourcesCPURequest", baseCpuUse);
+
+ const baseMemoryUse = Math.max(2, floor(maxMemory / 2));
+ updateField("resourcesMemoryRequest", baseMemoryUse);
+ })
+ .catch((err: any) => {
+ updateField("maxMemorySize", 0);
+ updateField("resourcesCPURequest", "");
+
+ console.error(err);
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [nodes, updateField]);
+
+ /*Calculate Allocation End*/
+
+ return (
+
+
+
+
Resources
+
+ You may specify the amount of CPU and Memory that MinIO servers
+ should reserve on each node.
+
+
+
+ {resourcesSize.error !== "" && (
+
+ {resourcesSize.error}
+
+ )}
+
+
+ {
+ let value = parseInt(e.target.value);
+ if (e.target.value === "") {
+ updateField("resourcesCPURequestError", "");
+ } else if (isNaN(value)) {
+ updateField("resourcesCPURequestError", "Invalid number");
+ } else if (value > parseInt(maxCPUsUse)) {
+ updateField(
+ "resourcesCPURequestError",
+ `Request exceeds available cores (${maxCPUsUse})`
+ );
+ } else if (e.target.validity.valid) {
+ updateField("resourcesCPURequestError", "");
+ } else {
+ updateField("resourcesCPURequestError", "Invalid configuration");
+ }
+ updateField("resourcesCPURequest", e.target.value);
+ }}
+ value={resourcesCPURequest}
+ disabled={selectedStorageClass === ""}
+ max={maxCPUsUse}
+ error={resourcesCPURequestError}
+ pattern={"[0-9]*"}
+ />
+
+
+
+ ) => {
+ let value = parseInt(e.target.value);
+ if (e.target.value === "") {
+ updateField("resourcesMemoryRequestError", "");
+ } else if (isNaN(value)) {
+ updateField("resourcesMemoryRequestError", "Invalid number");
+ } else if (value > parseInt(maxMemorySize)) {
+ updateField(
+ "resourcesMemoryRequestError",
+ `Request exceeds available memory across ${nodes} nodes (${maxMemorySize}Gi)`
+ );
+ } else if (value < 2) {
+ updateField(
+ "resourcesMemoryRequestError",
+ "At least 2Gi must be requested"
+ );
+ } else if (e.target.validity.valid) {
+ updateField("resourcesMemoryRequestError", "");
+ } else {
+ updateField(
+ "resourcesMemoryRequestError",
+ "Invalid configuration"
+ );
+ }
+ updateField("resourcesMemoryRequest", e.target.value);
+ }}
+ label="Memory Request [Gi]"
+ value={resourcesMemoryRequest}
+ disabled={selectedStorageClass === ""}
+ error={resourcesMemoryRequestError}
+ pattern={"[0-9]*"}
+ />
+
+
+
+ {
+ const targetD = e.target;
+ const checked = targetD.checked;
+
+ updateField("resourcesSpecifyLimit", checked);
+ }}
+ label={"Specify Limit"}
+ />
+
+
+ {resourcesSpecifyLimit && (
+
+
+ {
+ let value = parseInt(e.target.value);
+ if (e.target.value === "") {
+ updateField("resourcesCPULimitError", "");
+ } else if (isNaN(value)) {
+ updateField("resourcesCPULimitError", "Invalid number");
+ } else if (e.target.validity.valid) {
+ updateField("resourcesCPULimitError", "");
+ } else {
+ updateField(
+ "resourcesCPULimitError",
+ "Invalid configuration"
+ );
+ }
+ updateField("resourcesCPULimit", e.target.value);
+ }}
+ value={resourcesCPULimit}
+ disabled={selectedStorageClass === ""}
+ max={maxCPUsUse}
+ error={resourcesCPULimitError}
+ pattern={"[0-9]*"}
+ />
+
+
+
+ ) => {
+ let value = parseInt(e.target.value);
+ console.log("value", value);
+ if (e.target.value === "") {
+ updateField("resourcesMemoryLimitError", "");
+ } else if (isNaN(value)) {
+ updateField("resourcesMemoryLimitError", "Invalid number");
+ } else if (e.target.validity.valid) {
+ updateField("resourcesMemoryLimitError", "");
+ } else {
+ updateField(
+ "resourcesMemoryLimitError",
+ "Invalid configuration"
+ );
+ }
+ updateField("resourcesMemoryLimit", e.target.value);
+ }}
+ label="Memory Limit [Gi]"
+ value={resourcesMemoryLimit}
+ disabled={selectedStorageClass === ""}
+ error={resourcesMemoryLimitError}
+ pattern={"[0-9]*"}
+ />
+
+
+ )}
+
+ );
+};
+
+const mapState = (state: AppState) => ({
+ nodes: state.tenants.createTenant.fields.tenantSize.nodes,
+
+ resourcesSize: state.tenants.createTenant.fields.tenantSize.resourcesSize,
+ selectedStorageClass:
+ state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
+ maxAllocatableResources:
+ state.tenants.createTenant.fields.tenantSize.maxAllocatableResources,
+ maxCPUsUse: state.tenants.createTenant.fields.tenantSize.maxCPUsUse,
+ maxMemorySize: state.tenants.createTenant.fields.tenantSize.maxMemorySize,
+
+ resourcesSpecifyLimit:
+ state.tenants.createTenant.fields.tenantSize.resourcesSpecifyLimit,
+
+ resourcesCPURequestError:
+ state.tenants.createTenant.fields.tenantSize.resourcesCPURequestError,
+ resourcesCPURequest:
+ state.tenants.createTenant.fields.tenantSize.resourcesCPURequest,
+ resourcesCPULimitError:
+ state.tenants.createTenant.fields.tenantSize.resourcesCPULimitError,
+ resourcesCPULimit:
+ state.tenants.createTenant.fields.tenantSize.resourcesCPULimit,
+
+ resourcesMemoryRequestError:
+ state.tenants.createTenant.fields.tenantSize.resourcesMemoryRequestError,
+ resourcesMemoryRequest:
+ state.tenants.createTenant.fields.tenantSize.resourcesMemoryRequest,
+ resourcesMemoryLimitError:
+ state.tenants.createTenant.fields.tenantSize.resourcesMemoryLimitError,
+ resourcesMemoryLimit:
+ state.tenants.createTenant.fields.tenantSize.resourcesMemoryLimit,
+});
+
+const connector = connect(mapState, {
+ updateAddField,
+ isPageValid,
+});
+
+export default withStyles(styles)(connector(TenantSizeResources));
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/utils.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/utils.tsx
new file mode 100644
index 000000000..1ab515580
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantResources/utils.tsx
@@ -0,0 +1,84 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 MinIO, Inc.
+//
+// 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 React from "react";
+import { Opts } from "../../../ListTenants/utils";
+import TenantSizeMK from "./TenantSizeMK";
+
+export enum IMkEnvs {
+ "aws",
+ "azure",
+ "gcs",
+ "default",
+ undefined,
+}
+
+export interface IDriveSizing {
+ driveSize: string;
+ sizeUnit: string;
+}
+
+export interface IntegrationConfiguration {
+ typeSelection: string;
+ storageClass: string;
+ CPU: number;
+ memory: number;
+ drivesPerServer: number;
+ driveSize: IDriveSizing;
+}
+
+export const AWSStorageTypes: Opts[] = [
+ { label: "NVME", value: "nvme" },
+ { label: "HDD", value: "hdd" },
+];
+
+export const resourcesConfigurations = {
+ "mp-mode-aws": IMkEnvs.aws,
+ "mp-mode-azure": IMkEnvs.azure,
+ "mp-mode-gcs": IMkEnvs.gcs,
+};
+
+export const AWSConfigurations: IntegrationConfiguration[] = [
+ {
+ typeSelection: "nvme",
+ storageClass: "nvme-i3en-12xlarge",
+ CPU: 48,
+ memory: 384,
+ driveSize: { driveSize: "7500", sizeUnit: "Gi" },
+ drivesPerServer: 4,
+ },
+ {
+ typeSelection: "hdd",
+ storageClass: "hdd-d3en-12xlarge",
+ CPU: 8,
+ memory: 32,
+ driveSize: { driveSize: "12.7", sizeUnit: "Ti" },
+ drivesPerServer: 4,
+ },
+];
+
+export const mkPanelConfigurations = {
+ [IMkEnvs.aws]: {
+ variantSelectorLabel: "Storage Type",
+ variantSelectorValues: AWSStorageTypes,
+ configurations: AWSConfigurations,
+ sizingComponent: ,
+ },
+ [IMkEnvs.azure]: {},
+ [IMkEnvs.gcs]: {},
+ [IMkEnvs.default]: {},
+ [IMkEnvs.undefined]: {},
+};
diff --git a/portal-ui/src/screens/Console/Tenants/actions.ts b/portal-ui/src/screens/Console/Tenants/actions.ts
index 49450cde0..017599b99 100644
--- a/portal-ui/src/screens/Console/Tenants/actions.ts
+++ b/portal-ui/src/screens/Console/Tenants/actions.ts
@@ -17,32 +17,31 @@
import { ITenant } from "./ListTenants/types";
import { Opts } from "./ListTenants/utils";
import {
- ADD_TENANT_SET_ADVANCED_MODE,
+ ADD_TENANT_ADD_CA_KEYPAIR,
+ ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_ADD_CONSOLE_CERT,
+ ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
+ ADD_TENANT_ADD_FILE_TO_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
+ ADD_TENANT_ADD_MINIO_KEYPAIR,
+ ADD_TENANT_DELETE_CA_KEYPAIR,
+ ADD_TENANT_DELETE_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_DELETE_MINIO_KEYPAIR,
+ ADD_TENANT_ENCRYPTION_CLIENT_CERT,
+ ADD_TENANT_ENCRYPTION_GEMALTO_CA,
+ ADD_TENANT_ENCRYPTION_SERVER_CERT,
+ ADD_TENANT_ENCRYPTION_VAULT_CA,
+ ADD_TENANT_ENCRYPTION_VAULT_CERT,
+ ADD_TENANT_RESET_FORM,
ADD_TENANT_SET_CURRENT_PAGE,
- ADD_TENANT_UPDATE_FIELD,
+ ADD_TENANT_SET_LIMIT_SIZE,
ADD_TENANT_SET_PAGE_VALID,
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
- ADD_TENANT_SET_LIMIT_SIZE,
- ADD_TENANT_ADD_CA_KEYPAIR,
- ADD_TENANT_DELETE_CA_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
- ADD_TENANT_ADD_MINIO_KEYPAIR,
- ADD_TENANT_DELETE_MINIO_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
- ADD_TENANT_ADD_CONSOLE_CERT,
- ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_DELETE_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_ENCRYPTION_SERVER_CERT,
- ADD_TENANT_ENCRYPTION_CLIENT_CERT,
- ADD_TENANT_ENCRYPTION_VAULT_CERT,
- ADD_TENANT_ENCRYPTION_VAULT_CA,
- ADD_TENANT_ENCRYPTION_GEMALTO_CA,
- ADD_TENANT_RESET_FORM,
- TENANT_DETAILS_SET_LOADING,
- TENANT_DETAILS_SET_TENANT,
+ ADD_TENANT_UPDATE_FIELD,
TENANT_DETAILS_SET_CURRENT_TENANT,
+ TENANT_DETAILS_SET_LOADING,
TENANT_DETAILS_SET_TAB,
+ TENANT_DETAILS_SET_TENANT,
} from "./types";
// Basic actions
@@ -53,13 +52,6 @@ export const setWizardPage = (page: number) => {
};
};
-export const setAdvancedMode = (state: boolean) => {
- return {
- type: ADD_TENANT_SET_ADVANCED_MODE,
- state,
- };
-};
-
export const updateAddField = (
pageName: string,
fieldName: string,
diff --git a/portal-ui/src/screens/Console/Tenants/reducer.ts b/portal-ui/src/screens/Console/Tenants/reducer.ts
index 827bdadaa..3dd0a97d8 100644
--- a/portal-ui/src/screens/Console/Tenants/reducer.ts
+++ b/portal-ui/src/screens/Console/Tenants/reducer.ts
@@ -16,34 +16,33 @@
import has from "lodash/has";
import get from "lodash/get";
import {
- TenantsManagementTypes,
- ITenantState,
+ ADD_TENANT_ADD_CA_KEYPAIR,
+ ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_ADD_CONSOLE_CERT,
+ ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
+ ADD_TENANT_ADD_FILE_TO_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
+ ADD_TENANT_ADD_MINIO_KEYPAIR,
+ ADD_TENANT_DELETE_CA_KEYPAIR,
+ ADD_TENANT_DELETE_CONSOLE_CA_KEYPAIR,
+ ADD_TENANT_DELETE_MINIO_KEYPAIR,
+ ADD_TENANT_ENCRYPTION_CLIENT_CERT,
+ ADD_TENANT_ENCRYPTION_GEMALTO_CA,
+ ADD_TENANT_ENCRYPTION_SERVER_CERT,
+ ADD_TENANT_ENCRYPTION_VAULT_CA,
+ ADD_TENANT_ENCRYPTION_VAULT_CERT,
+ ADD_TENANT_RESET_FORM,
ADD_TENANT_SET_CURRENT_PAGE,
- ADD_TENANT_SET_ADVANCED_MODE,
- ADD_TENANT_UPDATE_FIELD,
+ ADD_TENANT_SET_LIMIT_SIZE,
ADD_TENANT_SET_PAGE_VALID,
ADD_TENANT_SET_STORAGE_CLASSES_LIST,
- ADD_TENANT_ADD_MINIO_KEYPAIR,
- ADD_TENANT_DELETE_MINIO_KEYPAIR,
- ADD_TENANT_ADD_CA_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_CA_KEYPAIR,
- ADD_TENANT_DELETE_CA_KEYPAIR,
- ADD_TENANT_ADD_CONSOLE_CERT,
- ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_DELETE_CONSOLE_CA_KEYPAIR,
- ADD_TENANT_ADD_FILE_TO_MINIO_KEYPAIR,
- ADD_TENANT_ENCRYPTION_SERVER_CERT,
- ADD_TENANT_ENCRYPTION_CLIENT_CERT,
- ADD_TENANT_ENCRYPTION_VAULT_CERT,
- ADD_TENANT_ENCRYPTION_VAULT_CA,
- ADD_TENANT_ENCRYPTION_GEMALTO_CA,
- ADD_TENANT_RESET_FORM,
- ADD_TENANT_SET_LIMIT_SIZE,
- TENANT_DETAILS_SET_LOADING,
+ ADD_TENANT_UPDATE_FIELD,
+ ITenantState,
TENANT_DETAILS_SET_CURRENT_TENANT,
- TENANT_DETAILS_SET_TENANT,
+ TENANT_DETAILS_SET_LOADING,
TENANT_DETAILS_SET_TAB,
+ TENANT_DETAILS_SET_TENANT,
+ TenantsManagementTypes,
} from "./types";
import { KeyPair } from "./ListTenants/utils";
import { getRandomString } from "./utils";
@@ -61,7 +60,6 @@ const initialState: ITenantState = {
"security",
"encryption",
],
- advancedModeOn: false,
storageClasses: [],
limitSize: {},
fields: {
@@ -69,6 +67,7 @@ const initialState: ITenantState = {
tenantName: "",
namespace: "",
selectedStorageClass: "",
+ selectedStorageType: "",
},
configure: {
customImage: true,
@@ -90,8 +89,8 @@ const initialState: ITenantState = {
logSearchPostgresInitImage: "",
prometheusVolumeSize: "5",
prometheusSizeFactor: "Gi",
- logSearchSelectedStorageClass: "",
- prometheusSelectedStorageClass: "",
+ logSearchSelectedStorageClass: "default",
+ prometheusSelectedStorageClass: "default",
prometheusImage: "",
prometheusSidecarImage: "",
prometheusInitImage: "",
@@ -200,8 +199,17 @@ const initialState: ITenantState = {
ecParity: "",
ecParityChoices: [],
cleanECChoices: [],
- maxAllocableMemo: 0,
cpuToUse: "0",
+ // resource request
+ resourcesSpecifyLimit: false,
+ resourcesCPURequestError: "",
+ resourcesCPURequest: "",
+ resourcesCPULimitError: "",
+ resourcesCPULimit: "",
+ resourcesMemoryRequestError: "",
+ resourcesMemoryRequest: "",
+ resourcesMemoryLimitError: "",
+ resourcesMemoryLimit: "",
resourcesSize: {
error: "",
memoryRequest: 0,
@@ -214,7 +222,6 @@ const initialState: ITenantState = {
nodes: 0,
persistentVolumes: 0,
disks: 0,
- volumePerDisk: 0,
},
ecParityCalc: {
error: 0,
@@ -239,6 +246,14 @@ const initialState: ITenantState = {
},
maxCPUsUse: "0",
maxMemorySize: "0",
+ integrationSelection: {
+ driveSize: { driveSize: "0", sizeUnit: "B" },
+ CPU: 0,
+ typeSelection: "",
+ memory: 0,
+ drivesPerServer: 0,
+ storageClass: "",
+ },
},
affinity: {
nodeSelectorLabels: "",
@@ -338,10 +353,7 @@ export function tenantsReducer(
newState.createTenant.page = action.page;
return { ...newState };
- case ADD_TENANT_SET_ADVANCED_MODE:
- newState.createTenant.advancedModeOn = action.state;
- return { ...newState };
case ADD_TENANT_UPDATE_FIELD:
if (
has(newState.createTenant.fields, `${action.pageName}.${action.field}`)
@@ -599,7 +611,6 @@ export function tenantsReducer(
"security",
"encryption",
],
- advancedModeOn: false,
storageClasses: [],
limitSize: {},
fields: {
@@ -607,6 +618,7 @@ export function tenantsReducer(
tenantName: "",
namespace: "",
selectedStorageClass: "",
+ selectedStorageType: "",
},
configure: {
customImage: false,
@@ -622,14 +634,14 @@ export function tenantsReducer(
prometheusCustom: false,
logSearchVolumeSize: "5",
logSearchSizeFactor: "Gi",
- logSearchSelectedStorageClass: "",
+ logSearchSelectedStorageClass: "default",
logSearchImage: "",
kesImage: "",
logSearchPostgresImage: "",
logSearchPostgresInitImage: "",
prometheusVolumeSize: "5",
prometheusSizeFactor: "Gi",
- prometheusSelectedStorageClass: "",
+ prometheusSelectedStorageClass: "default",
prometheusImage: "",
prometheusSidecarImage: "",
prometheusInitImage: "",
@@ -738,13 +750,11 @@ export function tenantsReducer(
ecParity: "",
ecParityChoices: [],
cleanECChoices: [],
- maxAllocableMemo: 0,
distribution: {
error: "",
nodes: 0,
persistentVolumes: 0,
disks: 0,
- volumePerDisk: 0,
},
ecParityCalc: {
error: 0,
@@ -756,6 +766,16 @@ export function tenantsReducer(
},
limitSize: {},
cpuToUse: "0",
+ // resource request
+ resourcesSpecifyLimit: false,
+ resourcesCPURequestError: "",
+ resourcesCPURequest: "",
+ resourcesCPULimitError: "",
+ resourcesCPULimit: "",
+ resourcesMemoryRequestError: "",
+ resourcesMemoryRequest: "",
+ resourcesMemoryLimitError: "",
+ resourcesMemoryLimit: "",
resourcesSize: {
error: "",
memoryRequest: 0,
@@ -777,6 +797,14 @@ export function tenantsReducer(
},
maxCPUsUse: "0",
maxMemorySize: "0",
+ integrationSelection: {
+ driveSize: { driveSize: "0", sizeUnit: "B" },
+ CPU: 0,
+ typeSelection: "",
+ memory: 0,
+ drivesPerServer: 0,
+ storageClass: "",
+ },
},
affinity: {
nodeSelectorLabels: "",
diff --git a/portal-ui/src/screens/Console/Tenants/types.ts b/portal-ui/src/screens/Console/Tenants/types.ts
index af0165eb4..2f8fce72b 100644
--- a/portal-ui/src/screens/Console/Tenants/types.ts
+++ b/portal-ui/src/screens/Console/Tenants/types.ts
@@ -15,8 +15,9 @@
// along with this program. If not, see .
import { IErasureCodeCalc } from "../../../common/types";
-import { ITenant, IResourcesSize } from "./ListTenants/types";
+import { IResourcesSize, ITenant } from "./ListTenants/types";
import { KeyPair, Opts } from "./ListTenants/utils";
+import { IntegrationConfiguration } from "./AddTenant/Steps/TenantResources/utils";
export const ADD_TENANT_SET_CURRENT_PAGE = "ADD_TENANT/SET_CURRENT_PAGE";
export const ADD_TENANT_SET_ADVANCED_MODE = "ADD_TENANT/SET_ADVANCED_MODE";
@@ -64,6 +65,7 @@ export const TENANT_DETAILS_SET_CURRENT_TENANT =
"TENANT_DETAILS/SET_CURRENT_TENANT";
export const TENANT_DETAILS_SET_TENANT = "TENANT_DETAILS/SET_TENANT";
export const TENANT_DETAILS_SET_TAB = "TENANT_DETAILS/SET_TAB";
+
export interface ICertificateInfo {
name: string;
serialNumber: string;
@@ -86,7 +88,6 @@ export interface ITenantSecurityResponse {
export interface ICreateTenant {
page: number;
validPages: string[];
- advancedModeOn: boolean;
storageClasses: Opts[];
limitSize: any;
fields: IFieldStore;
@@ -119,6 +120,7 @@ export interface INameTenantFields {
tenantName: string;
namespace: string;
selectedStorageClass: string;
+ selectedStorageType: string;
}
export interface ISecurityContext {
@@ -237,7 +239,6 @@ export interface ITenantSizeFields {
ecParity: string;
ecParityChoices: Opts[];
cleanECChoices: string[];
- maxAllocableMemo: number;
resourcesSize: IResourcesSize;
distribution: any;
ecParityCalc: IErasureCodeCalc;
@@ -246,6 +247,19 @@ export interface ITenantSizeFields {
maxAllocatableResources: AllocableResourcesResponse;
maxCPUsUse: string;
maxMemorySize: string;
+ integrationSelection: IntegrationConfiguration;
+
+ resourcesSpecifyLimit: boolean;
+
+ resourcesCPURequestError: string;
+ resourcesCPURequest: string;
+ resourcesCPULimitError: string;
+ resourcesCPULimit: string;
+
+ resourcesMemoryRequestError: string;
+ resourcesMemoryRequest: string;
+ resourcesMemoryLimitError: string;
+ resourcesMemoryLimit: string;
}
export interface ITenantAffinity {
@@ -289,11 +303,6 @@ interface SetTenantWizardPage {
page: number;
}
-interface SetAdvancedMode {
- type: typeof ADD_TENANT_SET_ADVANCED_MODE;
- state: boolean;
-}
-
interface UpdateATField {
type: typeof ADD_TENANT_UPDATE_FIELD;
pageName: keyof IFieldStore;
@@ -333,6 +342,7 @@ interface DeleteMinioKeyPair {
type: typeof ADD_TENANT_DELETE_MINIO_KEYPAIR;
id: string;
}
+
interface AddCAKeyPair {
type: typeof ADD_TENANT_ADD_CA_KEYPAIR;
}
@@ -349,6 +359,7 @@ interface DeleteCAKeyPair {
type: typeof ADD_TENANT_DELETE_CA_KEYPAIR;
id: string;
}
+
interface AddConsoleCAKeyPair {
type: typeof ADD_TENANT_ADD_CONSOLE_CA_KEYPAIR;
}
@@ -436,7 +447,6 @@ export type FieldsToHandle = INameTenantFields;
export type TenantsManagementTypes =
| SetTenantWizardPage
- | SetAdvancedMode
| UpdateATField
| SetPageValid
| SetStorageClassesList
diff --git a/swagger-operator.yml b/swagger-operator.yml
index f9bc8c174..e37724871 100644
--- a/swagger-operator.yml
+++ b/swagger-operator.yml
@@ -998,6 +998,10 @@ definitions:
operatorSessionResponse:
type: object
properties:
+ features:
+ type: array
+ items:
+ type: string
status:
type: string
enum: [ok]