// Copyright 2021 99cloud // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { inject, observer } from 'mobx-react'; import { StepAction } from 'containers/Action'; import globalFlavorStore from 'stores/nova/flavor'; import { categoryHasIOPS, categoryHasEphemeral, isGpuCategory, isComputeOptimized, isGpuVisual, isBareMetal, } from 'resources/nova/flavor'; import { parse } from 'qs'; import AccessTypeSetting from './AccessTypeSetting'; import ParamSetting from './ParamSetting'; export class StepCreate extends StepAction { static id = 'flavor-create'; static title = t('Create Flavor'); static path = '/compute/flavor-admin/create'; static policy = 'os_compute_api:os-flavor-manage:create'; static allowed() { return Promise.resolve(true); } get tab() { const params = parse(this.routing.location.search.slice(1)); const { tab } = params; return tab; } get listUrl() { const { architecture } = this.values || {}; const tab = architecture || this.tab; return this.getRoutePath('flavor', null, { tab }); } get name() { return t('create flavor'); } get hasConfirmStep() { return false; } init() { this.store = globalFlavorStore; } get steps() { return [ { title: t('Params Setting'), component: ParamSetting, }, { title: t('Access Type Setting'), component: AccessTypeSetting, }, ]; } getProperties = (architecture, category, settings) => { const settingItem = settings.find((it) => it.key === 'flavor_families'); const archItem = settingItem.value.find( (it) => it.architecture === architecture ); const categoryItem = archItem.categories.find((it) => it.name === category); const obj = {}; (categoryItem.properties || []).forEach((it) => { obj[it.key] = it.value; }); return obj; }; getPageSizeValue(type, value) { return type === 'custom' ? value : type; } onSubmit = (values) => { const { architecture, category, name, vcpus, memoryGb, bandwidth, iops, ephemeral, gpuType, gpuNumber, numaNodesNum, memPageSizeValueMore, memPageSizeValue, memPageSizeMore, attachUsb, usbType, usbNumber, numaNodes, cpuPolicy, cpuThreadPolicy, memPageSize, settings = [], accessType, accessControl = {}, resourceProps, traitProps, } = values; const body = { name, vcpus, ram: memoryGb * 1024, disk: 0, }; const isPublic = accessType === 'public'; body['os-flavor-access:is_public'] = isPublic; if (categoryHasEphemeral) { body['OS-FLV-EXT-DATA:ephemeral'] = ephemeral; } const properties = this.getProperties(architecture, category, settings); const extraSpecs = { ':architecture': architecture, ':category': category, ...properties, }; if (bandwidth) { extraSpecs['quota:vif_outbound_average'] = bandwidth * 1000 * 125; extraSpecs['quota:vif_inbound_average'] = bandwidth * 1000 * 125; } if (categoryHasIOPS(category) && iops) { extraSpecs['quota:disk_total_iops_sec'] = iops; } const isGPUType = isGpuCategory(category); const isGpuVisualType = isGpuVisual(category); const isGpuComputeType = isGPUType && !isGpuVisualType; if (attachUsb) { extraSpecs['pci_passthrough:alias'] = `${usbType}:${usbNumber}`; } if (isGpuComputeType) { extraSpecs['pci_passthrough:alias'] = attachUsb ? `${gpuType}:${gpuNumber},${usbType}:${usbNumber}` : `${gpuType}:${gpuNumber}`; } else if (isGpuVisualType) { extraSpecs['resources:VGPU'] = gpuNumber; } if (isComputeOptimized(category)) { extraSpecs['hw:numa_nodes'] = numaNodes.length; numaNodes.forEach((item, index) => { extraSpecs[`hw:numa_cpus.${index}`] = item.value.cpu; extraSpecs[`hw:numa_mem.${index}`] = item.value.ram; }); extraSpecs['hw:cpu_policy'] = cpuPolicy; extraSpecs['hw:cpu_thread_policy'] = cpuThreadPolicy; extraSpecs['hw:mem_page_size'] = this.getPageSizeValue( memPageSize, memPageSizeValue ); } else if (architecture !== 'bare_metal') { extraSpecs['hw:numa_nodes'] = numaNodesNum; extraSpecs['hw:mem_page_size'] = this.getPageSizeValue( memPageSizeMore, memPageSizeValueMore ); extraSpecs['hw:live_resize'] = 'True'; } if (isBareMetal(architecture)) { resourceProps.forEach((it) => { const { key, value } = it.value; extraSpecs[`resources:${key}`] = value; }); traitProps.forEach((it) => { const { value } = it; extraSpecs[`trait:${value}`] = 'required'; }); } return this.store.create( body, extraSpecs, !isPublic && (accessControl.selectedRowKeys || []) ); }; } export default inject('rootStore')(observer(StepCreate));