| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you 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. | |
| <template> | |
| <div class="form-layout" v-ctrl-enter="handleSubmit"> | |
| <a-spin :spinning="loading"> | |
| <a-alert | |
| v-if="resource" | |
| type="info" | |
| style="margin-bottom: 16px"> | |
| <template #message> | |
| <div style="display: block; width: 100%;"> | |
| <div style="display: block; margin-bottom: 8px;"> | |
| <strong>{{ $t('message.clone.offering.from') }}: {{ resource.name }}</strong> | |
| </div> | |
| <div style="display: block; font-size: 12px;"> | |
| {{ $t('message.clone.offering.edit.hint') }} | |
| </div> | |
| </div> | |
| </template> | |
| </a-alert> | |
| <a-form | |
| :ref="formRef" | |
| :model="form" | |
| :rules="rules" | |
| @finish="handleSubmit" | |
| layout="vertical" | |
| > | |
| <a-form-item name="name" ref="name"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/> | |
| </template> | |
| <a-input | |
| v-focus="true" | |
| v-model:value="form.name" | |
| :placeholder="apiParams.name.description"/> | |
| </a-form-item> | |
| <a-form-item name="displaytext" ref="displaytext"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.displaytext')" :tooltip="apiParams.displaytext.description"/> | |
| </template> | |
| <a-input | |
| v-model:value="form.displaytext" | |
| :placeholder="apiParams.displaytext.description"/> | |
| </a-form-item> | |
| <a-form-item name="guestiptype" ref="guestiptype"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.guestiptype')" :tooltip="apiParams.guestiptype.description"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.guestiptype" | |
| buttonStyle="solid" | |
| @change="selected => { handleGuestTypeChange(selected.target.value) }"> | |
| <a-radio-button value="isolated"> | |
| {{ $t('label.isolated') }} | |
| </a-radio-button> | |
| <a-radio-button value="l2"> | |
| {{ $t('label.l2') }} | |
| </a-radio-button> | |
| <a-radio-button value="shared"> | |
| {{ $t('label.shared') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="internetprotocol" ref="internetprotocol" v-if="guestType === 'isolated'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.internetprotocol')" :tooltip="apiParams.internetprotocol.description"/> | |
| </template> | |
| <span v-if="!ipv6NetworkOfferingEnabled || internetProtocolValue!=='ipv4'"> | |
| <a-alert type="warning"> | |
| <template #message> | |
| <span v-html="ipv6NetworkOfferingEnabled ? $t('message.offering.internet.protocol.warning') : $t('message.offering.ipv6.warning')" /> | |
| </template> | |
| </a-alert> | |
| <br/> | |
| </span> | |
| <a-radio-group | |
| v-model:value="form.internetprotocol" | |
| :disabled="!ipv6NetworkOfferingEnabled" | |
| buttonStyle="solid" | |
| @change="e => { internetProtocolValue = e.target.value }"> | |
| <a-radio-button value="ipv4"> | |
| {{ $t('label.ip.v4') }} | |
| </a-radio-button> | |
| <a-radio-button value="dualstack"> | |
| {{ $t('label.ip.v4.v6') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-row :gutter="12"> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="forvpc" ref="forvpc" v-if="guestType === 'isolated'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.vpc')" :tooltip="apiParams.forvpc.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.forvpc" @change="handleForVpcChange" /> | |
| </a-form-item> | |
| </a-col> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="provider" ref="provider"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.provider')" :tooltip="apiParams.provider.description"/> | |
| </template> | |
| <a-select | |
| v-model:value="form.provider" | |
| disabled | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :placeholder="apiParams.provider.description"> | |
| <a-select-option key="" value="">{{ $t('label.none') }}</a-select-option> | |
| <a-select-option :value="'NSX'" :label="$t('label.nsx')">{{ $t('label.nsx') }}</a-select-option> | |
| <a-select-option :value="'Netris'" :label="$t('label.netris')">{{ $t('label.netris') }}</a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| </a-col> | |
| </a-row> | |
| <!-- NSX specific toggles (mirror AddNetworkOffering.vue) --> | |
| <a-row :gutter="12" v-if="form.provider === 'NSX'"> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="nsxsupportlb" ref="nsxsupportlb" v-if="guestType === 'isolated'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.nsx.supports.lb')" :tooltip="apiParams.nsxsupportlb.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.nsxsupportlb" @change="val => { handleNsxLbService(val) }" /> | |
| </a-form-item> | |
| </a-col> | |
| <a-col :md="12" :lg="12" v-if="form.nsxsupportlb && form.forvpc"> | |
| <a-form-item name="nsxsupportsinternallb" ref="nsxsupportsinternallb" v-if="guestType === 'isolated'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.nsx.supports.internal.lb')" :tooltip="apiParams.nsxsupportsinternallb.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.nsxsupportsinternallb"/> | |
| </a-form-item> | |
| </a-col> | |
| </a-row> | |
| <a-form-item name="networkmode" ref="networkmode" v-if="guestType === 'isolated' && form.provider"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.networkmode')" :tooltip="apiParams.networkmode.description"/> | |
| </template> | |
| <a-select | |
| v-model:value="form.networkmode" | |
| @change="val => { handleForNetworkModeChange(val) }" | |
| :disabled="provider === 'NSX' || provider === 'Netris'" | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :placeholder="apiParams.networkmode.description"> | |
| <a-select-option value="NATTED" :label="'NATTED'">NATTED</a-select-option> | |
| <a-select-option value="ROUTED" :label="'ROUTED'">ROUTED</a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item name="routingmode" ref="routingmode" v-if="networkmode === 'ROUTED' || internetProtocolValue === 'ipv6' || internetProtocolValue === 'dualstack'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.routingmode')" :tooltip="apiParams.routingmode.description"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.routingmode" | |
| buttonStyle="solid" | |
| @change="selected => { routingMode = selected.target.value }"> | |
| <a-radio-button value="static"> | |
| {{ $t('label.static') }} | |
| </a-radio-button> | |
| <a-radio-button value="dynamic"> | |
| {{ $t('label.dynamic') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="userdatal2" ref="userdatal2" :label="$t('label.user.data')" v-if="guestType === 'l2'"> | |
| <a-switch v-model:checked="form.userdatal2" /> | |
| </a-form-item> | |
| <a-form-item name="networkrate" ref="networkrate"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.networkrate')" :tooltip="apiParams.networkrate.description"/> | |
| </template> | |
| <a-input | |
| v-model:value="form.networkrate" | |
| :placeholder="apiParams.networkrate.description"/> | |
| </a-form-item> | |
| <a-row :gutter="12"> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="specifyvlan" ref="specifyvlan"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.specifyvlan')" :tooltip="apiParams.specifyvlan.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.specifyvlan" /> | |
| </a-form-item> | |
| </a-col> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="ispersistent" ref="ispersistent" v-if="guestType !== 'shared'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.ispersistent')" :tooltip="apiParams.ispersistent.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.ispersistent" /> | |
| </a-form-item> | |
| </a-col> | |
| </a-row> | |
| <a-row :gutter="12"> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="promiscuousmode" ref="promiscuousmode"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.promiscuousmode')" :tooltip="$t('message.network.offering.promiscuous.mode')"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.promiscuousmode" | |
| buttonStyle="solid"> | |
| <a-radio-button value=""> | |
| {{ $t('label.none') }} | |
| </a-radio-button> | |
| <a-radio-button value="true"> | |
| {{ $t('label.accept') }} | |
| </a-radio-button> | |
| <a-radio-button value="false"> | |
| {{ $t('label.reject') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="macaddresschanges" ref="macaddresschanges"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.macaddresschanges')" :tooltip="$t('message.network.offering.mac.address.changes')"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.macaddresschanges" | |
| buttonStyle="solid"> | |
| <a-radio-button value=""> | |
| {{ $t('label.none') }} | |
| </a-radio-button> | |
| <a-radio-button value="true"> | |
| {{ $t('label.accept') }} | |
| </a-radio-button> | |
| <a-radio-button value="false"> | |
| {{ $t('label.reject') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| </a-col> | |
| <a-col :md="12" :lg="12"> | |
| <a-form-item name="forgedtransmits" ref="forgedtransmits"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.forgedtransmits')" :tooltip="$t('message.network.offering.forged.transmits')"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.forgedtransmits" | |
| buttonStyle="solid"> | |
| <a-radio-button value=""> | |
| {{ $t('label.none') }} | |
| </a-radio-button> | |
| <a-radio-button value="true"> | |
| {{ $t('label.accept') }} | |
| </a-radio-button> | |
| <a-radio-button value="false"> | |
| {{ $t('label.reject') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="maclearning" ref="maclearning"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.maclearning')" :tooltip="$t('message.network.offering.mac.learning')"/> | |
| </template> | |
| <span v-if="form.maclearning !== ''"> | |
| <a-alert type="warning"> | |
| <template #message> | |
| <div v-html="$t('message.network.offering.mac.learning.warning')"></div> | |
| </template> | |
| </a-alert> | |
| <br/> | |
| </span> | |
| <a-radio-group | |
| v-model:value="form.maclearning" | |
| buttonStyle="solid"> | |
| <a-radio-button value=""> | |
| {{ $t('label.none') }} | |
| </a-radio-button> | |
| <a-radio-button value="true"> | |
| {{ $t('label.accept') }} | |
| </a-radio-button> | |
| <a-radio-button value="false"> | |
| {{ $t('label.reject') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| </a-col> | |
| </a-row> | |
| <a-form-item v-if="guestType !== 'l2'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.supportedservices')" :tooltip="apiParams.supportedservices ? apiParams.supportedservices.description : ''"/> | |
| </template> | |
| <div class="supported-services-container" scroll-to="last-child"> | |
| <a-spin v-if="!servicesReady" :spinning="true" /> | |
| <a-list v-else itemLayout="horizontal" :dataSource="supportedServices"> | |
| <template #renderItem="{item}"> | |
| <a-list-item> | |
| <CheckBoxSelectPair | |
| :key="`${item.name}-${item.selectedProvider || 'none'}-${item.defaultChecked}`" | |
| :resourceKey="item.name" | |
| :checkBoxLabel="item.description" | |
| :forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'" | |
| :defaultCheckBoxValue="item.defaultChecked" | |
| :defaultSelectValue="item.selectedProvider" | |
| :selectOptions="item.provider" | |
| @handle-checkselectpair-change="handleSupportedServiceChange"/> | |
| </a-list-item> | |
| </template> | |
| </a-list> | |
| </div> | |
| </a-form-item> | |
| <a-form-item name="lbtype" ref="lbtype" :label="$t('label.lbtype')" v-if="forVpc && lbServiceChecked"> | |
| <a-radio-group | |
| v-model:value="form.lbtype" | |
| buttonStyle="solid" | |
| @change="e => { handleLbTypeChange(e.target.value) }" > | |
| <a-radio-button value="publicLb"> | |
| {{ $t('label.public.lb') }} | |
| </a-radio-button> | |
| <a-radio-button value="internalLb"> | |
| {{ $t('label.internal.lb') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="serviceofferingid" ref="serviceofferingid" v-if="guestType !== 'l2' && isVirtualRouterForAtLeastOneService"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/> | |
| </template> | |
| <a-select | |
| showSearch | |
| optionFilterProp="label" | |
| v-model:value="form.serviceofferingid" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :loading="serviceOfferingLoading" | |
| :placeholder="apiParams.serviceofferingid.description"> | |
| <a-select-option | |
| v-for="(offering, index) in serviceOfferings" | |
| :value="offering.id" | |
| :label="offering.displaytext || offering.name" | |
| :key="index"> | |
| {{ offering.displaytext || offering.name }} | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item | |
| name="redundantroutercapability" | |
| ref="redundantroutercapability" | |
| :label="$t('label.redundantrouter')" | |
| v-if="(guestType === 'shared' || guestType === 'isolated') && sourceNatServiceChecked && !isVpcVirtualRouterForAtLeastOneService"> | |
| <a-switch v-model:checked="form.redundantroutercapability" /> | |
| </a-form-item> | |
| <a-form-item name="sourcenattype" ref="sourcenattype" :label="$t('label.sourcenattype')" v-if="(guestType === 'shared' || guestType === 'isolated') && sourceNatServiceChecked"> | |
| <a-radio-group | |
| v-model:value="form.sourcenattype" | |
| buttonStyle="solid"> | |
| <a-radio-button value="peraccount"> | |
| {{ $t('label.per.account') }} | |
| </a-radio-button> | |
| <a-radio-button value="perzone"> | |
| {{ $t('label.per.zone') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item | |
| name="vmautoscalingcapability" | |
| ref="vmautoscalingcapability" | |
| :label="$t('label.supportsvmautoscaling')" | |
| v-if="lbServiceChecked && ['Netscaler', 'VirtualRouter', 'VpcVirtualRouter'].includes(lbServiceProvider)"> | |
| <a-switch v-model:checked="form.vmautoscalingcapability" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="elasticlb" | |
| ref="elasticlb" | |
| :label="$t('label.service.lb.elasticlbcheckbox')" | |
| v-if="guestType === 'shared' && lbServiceChecked && lbServiceProvider === 'Netscaler'"> | |
| <a-switch v-model:checked="form.elasticlb" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="inlinemode" | |
| ref="inlinemode" | |
| :label="$t('label.service.lb.inlinemodedropdown')" | |
| v-if="['shared', 'isolated'].includes(guestType) && lbServiceChecked && firewallServiceChecked && ['F5BigIp', 'Netscaler'].includes(lbServiceProvider) && ['JuniperSRX'].includes(firewallServiceProvider)"> | |
| <a-radio-group | |
| v-model:value="form.inlinemode" | |
| buttonStyle="solid"> | |
| <a-radio-button value="false"> | |
| {{ $t('side.by.side') }} | |
| </a-radio-button> | |
| <a-radio-button value="true"> | |
| {{ $t('inline') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item | |
| name="netscalerservicepackages" | |
| ref="netscalerservicepackages" | |
| :label="$t('label.service.lb.netscaler.servicepackages')" | |
| v-if="(guestType === 'shared' || guestType === 'isolated') && lbServiceChecked && lbServiceProvider === 'Netscaler'"> | |
| <a-select | |
| v-model:value="form.netscalerservicepackages" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :loading="registeredServicePackageLoading" | |
| :placeholder="$t('label.service.lb.netscaler.servicepackages')"> | |
| <a-select-option v-for="(opt, optIndex) in registeredServicePackages" :key="optIndex" :label="opt.name || opt.description"> | |
| {{ opt.name || opt.description }} | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item | |
| name="netscalerservicepackagesdescription" | |
| ref="netscalerservicepackagesdescription" | |
| :label="$t('label.service.lb.netscaler.servicepackages.description')" | |
| v-if="(guestType === 'shared' || guestType === 'isolated') && lbServiceChecked && lbServiceProvider === 'Netscaler'"> | |
| <a-input | |
| v-model:value="form.netscalerservicepackagesdescription" | |
| :placeholder="$t('label.service.lb.netscaler.servicepackages.description')"/> | |
| </a-form-item> | |
| <a-form-item | |
| name="elasticip" | |
| ref="elasticip" | |
| :label="$t('label.service.staticnat.elasticipcheckbox')" | |
| v-if="guestType === 'shared' && staticNatServiceChecked && staticNatServiceProvider === 'Netscaler'"> | |
| <a-switch v-model:checked="form.elasticip" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="associatepublicip" | |
| ref="associatepublicip" | |
| :label="$t('label.service.staticnat.associatepublicip')" | |
| v-if="isElasticIp && staticNatServiceChecked && staticNatServiceProvider === 'Netscaler'"> | |
| <a-switch v-model:checked="form.associatepublicip" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="supportsstrechedl2subnet" | |
| ref="supportsstrechedl2subnet" | |
| :label="$t('label.supportsstrechedl2subnet')" | |
| v-if="connectivityServiceChecked"> | |
| <a-switch v-model:checked="form.supportsstrechedl2subnet" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="conservemode" | |
| ref="conservemode" | |
| v-if="guestType === 'shared' || guestType === 'isolated'"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.conservemode')" :tooltip="apiParams.conservemode.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.conservemode" /> | |
| </a-form-item> | |
| <a-form-item name="tags" ref="tags"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.tags')" :tooltip="apiParams.tags.description"/> | |
| </template> | |
| <a-input | |
| v-model:value="form.tags" | |
| :placeholder="apiParams.tags.description"/> | |
| </a-form-item> | |
| <a-form-item name="availability" ref="availability" v-if="requiredNetworkOfferingExists && guestType === 'isolated' && sourceNatServiceChecked"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.availability')" :tooltip="apiParams.availability.description"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.availability" | |
| buttonStyle="solid"> | |
| <a-radio-button value="Optional"> | |
| {{ $t('label.optional') }} | |
| </a-radio-button> | |
| <a-radio-button value="Required"> | |
| {{ $t('label.required') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="egressdefaultpolicy" ref="egressdefaultpolicy"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.egressdefaultpolicy')" :tooltip="apiParams.egressdefaultpolicy.description"/> | |
| </template> | |
| <a-radio-group | |
| v-model:value="form.egressdefaultpolicy" | |
| buttonStyle="solid"> | |
| <a-radio-button value="allow"> | |
| {{ $t('label.allow') }} | |
| </a-radio-button> | |
| <a-radio-button value="deny"> | |
| {{ $t('label.deny') }} | |
| </a-radio-button> | |
| </a-radio-group> | |
| </a-form-item> | |
| <a-form-item name="specifyipranges" ref="specifyipranges"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.specifyipranges')" :tooltip="apiParams.specifyipranges.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.specifyipranges"/> | |
| </a-form-item> | |
| <a-form-item name="ispublic" ref="ispublic" :label="$t('label.ispublic')" v-show="isAdmin()"> | |
| <a-switch v-model:checked="form.ispublic" @change="val => { isPublic = val }" /> | |
| </a-form-item> | |
| <a-form-item name="domainid" ref="domainid" v-if="!isPublic"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.domainid')" :tooltip="apiParams.domainid.description"/> | |
| </template> | |
| <a-select | |
| mode="multiple" | |
| v-model:value="form.domainid" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :loading="domainLoading" | |
| :placeholder="apiParams.domainid.description"> | |
| <a-select-option v-for="(opt, optIndex) in domains" :key="optIndex" :label="opt.path || opt.name || opt.description"> | |
| <span> | |
| <resource-icon v-if="opt && opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/> | |
| <block-outlined v-else style="margin-right: 5px" /> | |
| {{ opt.path || opt.name || opt.description }} | |
| </span> | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item name="zoneid" ref="zoneid"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.zoneid')" :tooltip="apiParams.zoneid.description"/> | |
| </template> | |
| <a-select | |
| mode="multiple" | |
| v-model:value="form.zoneid" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" | |
| :loading="zoneLoading" | |
| :placeholder="apiParams.zoneid.description"> | |
| <a-select-option v-for="(opt, optIndex) in zones" :key="optIndex" :label="opt.name || opt.description"> | |
| <span> | |
| <resource-icon v-if="opt.icon" :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/> | |
| <global-outlined v-else style="margin-right: 5px"/> | |
| {{ opt.name || opt.description }} | |
| </span> | |
| </a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item name="enable" ref="enable" v-if="apiParams.enable"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.enable.network.offering')" :tooltip="apiParams.enable.description"/> | |
| </template> | |
| <a-switch v-model:checked="form.enable"/> | |
| </a-form-item> | |
| <div :span="24" class="action-button"> | |
| <a-button @click="closeAction">{{ $t('label.cancel') }}</a-button> | |
| <a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </a-form> | |
| </a-spin> | |
| </div> | |
| </template> | |
| <script> | |
| import { ref, reactive, toRaw } from 'vue' | |
| import { getAPI, postAPI } from '@/api' | |
| import { isAdmin } from '@/role' | |
| import { mixinForm } from '@/utils/mixin' | |
| import ResourceIcon from '@/components/view/ResourceIcon' | |
| import TooltipLabel from '@/components/widgets/TooltipLabel' | |
| import CheckBoxSelectPair from '@/components/CheckBoxSelectPair' | |
| import { BlockOutlined, GlobalOutlined } from '@ant-design/icons-vue' | |
| import { buildServiceCapabilityParams } from '@/composables/useServiceCapabilityParams' | |
| export default { | |
| name: 'CloneNetworkOffering', | |
| mixins: [mixinForm], | |
| components: { | |
| ResourceIcon, | |
| TooltipLabel, | |
| CheckBoxSelectPair, | |
| BlockOutlined, | |
| GlobalOutlined | |
| }, | |
| props: { | |
| resource: { | |
| type: Object, | |
| required: true | |
| } | |
| }, | |
| data () { | |
| return { | |
| loading: false, | |
| VPCVR: { | |
| name: 'VPCVirtualRouter', | |
| description: 'VPCVirtualRouter', | |
| enabled: true | |
| }, | |
| VR: { | |
| name: 'VirtualRouter', | |
| description: 'VirtualRouter', | |
| enabled: true | |
| }, | |
| NSX: { | |
| name: 'Nsx', | |
| description: 'Nsx', | |
| enabled: true | |
| }, | |
| Netris: { | |
| name: 'Netris', | |
| description: 'Netris', | |
| enabled: true | |
| }, | |
| nsxSupportedServicesMap: {}, | |
| netrisSupportedServicesMap: {}, | |
| // supported services backup | |
| supportedSvcs: [], | |
| guestType: 'isolated', | |
| forVpc: false, | |
| provider: '', | |
| networkmode: '', | |
| routingMode: 'static', | |
| internetProtocolValue: 'ipv4', | |
| networkmodes: [ | |
| { | |
| id: 0, | |
| name: 'NATTED' | |
| }, | |
| { | |
| id: 1, | |
| name: 'ROUTED' | |
| } | |
| ], | |
| supportedServices: [], | |
| supportedServiceLoading: false, | |
| servicesReady: false, | |
| selectedServiceProviderMap: {}, | |
| serviceProviderMap: {}, | |
| serviceOfferings: [], | |
| serviceOfferingLoading: false, | |
| isVirtualRouterForAtLeastOneService: false, | |
| isVpcVirtualRouterForAtLeastOneService: false, | |
| requiredNetworkOfferingExists: false, | |
| sourceNatServiceChecked: false, | |
| lbServiceChecked: false, | |
| lbServiceProvider: '', | |
| firewallServiceChecked: false, | |
| firewallServiceProvider: '', | |
| staticNatServiceChecked: false, | |
| staticNatServiceProvider: '', | |
| connectivityServiceChecked: false, | |
| isElasticIp: false, | |
| registeredServicePackages: [], | |
| registeredServicePackageLoading: false, | |
| isPublic: true, | |
| domains: [], | |
| domainLoading: false, | |
| zones: [], | |
| zoneLoading: false, | |
| ipv6NetworkOfferingEnabled: false | |
| } | |
| }, | |
| beforeCreate () { | |
| this.apiParams = this.$getApiParams('cloneNetworkOffering') | |
| }, | |
| watch: { | |
| 'form.elasticip' (newVal) { | |
| this.isElasticIp = newVal | |
| } | |
| }, | |
| async created () { | |
| this.zones = [ | |
| { | |
| id: null, | |
| name: this.$t('label.all.zone') | |
| } | |
| ] | |
| this.isPublic = isAdmin() | |
| this.initForm() | |
| await this.checkRequiredNetworkOfferingExists() | |
| this.fetchSupportedServices() | |
| this.fetchServiceOfferingData() | |
| this.fetchDomainData() | |
| this.fetchZoneData() | |
| this.fetchIpv6NetworkOfferingConfiguration() | |
| }, | |
| methods: { | |
| initForm () { | |
| this.formRef = ref() | |
| this.form = reactive({ | |
| guestiptype: this.guestType, | |
| internetprotocol: 'ipv4', | |
| forvpc: false, | |
| provider: '', | |
| networkmode: '', | |
| routingmode: 'static', | |
| lbtype: 'publicLb', | |
| userdatal2: false, | |
| availability: 'Optional', | |
| conservemode: true, | |
| ispersistent: false, | |
| specifyvlan: false, | |
| specifyipranges: false, | |
| egressdefaultpolicy: 'deny', | |
| enable: false, | |
| promiscuousmode: '', | |
| macaddresschanges: '', | |
| forgedtransmits: '', | |
| maclearning: '', | |
| ispublic: this.isPublic, | |
| domainid: [], | |
| zoneid: [], | |
| redundantroutercapability: false, | |
| sourcenattype: 'peraccount', | |
| vmautoscalingcapability: true, | |
| elasticlb: false, | |
| inlinemode: 'false', | |
| elasticip: false, | |
| associatepublicip: false, | |
| supportsstrechedl2subnet: false, | |
| isolation: 'dedicated', | |
| netscalerservicepackages: null, | |
| netscalerservicepackagesdescription: '', | |
| nsxsupportlb: true, | |
| nsxsupportsinternallb: false | |
| }) | |
| this.rules = reactive({ | |
| name: [{ required: true, message: this.$t('message.error.required.input') }], | |
| networkrate: [{ | |
| validator: async (rule, value) => { | |
| if (value && (isNaN(value) || value < 0)) { | |
| return Promise.reject(this.$t('message.validate.number')) | |
| } | |
| return Promise.resolve() | |
| } | |
| }], | |
| domainid: [{ type: 'array', required: true, message: this.$t('message.error.select') }], | |
| zoneid: [{ | |
| type: 'array', | |
| validator: async (rule, value) => { | |
| if (value && value.length > 1 && value.indexOf(0) !== -1) { | |
| return Promise.reject(this.$t('message.error.zone.combined')) | |
| } | |
| return Promise.resolve() | |
| } | |
| }] | |
| }) | |
| }, | |
| fetchServiceOfferingData () { | |
| this.serviceOfferingLoading = true | |
| getAPI('listServiceOfferings', { issystem: true, systemvmtype: 'domainrouter' }).then(json => { | |
| this.serviceOfferings = json.listserviceofferingsresponse.serviceoffering || [] | |
| }).finally(() => { | |
| this.serviceOfferingLoading = false | |
| }) | |
| }, | |
| fetchSupportedServices () { | |
| this.supportedServiceLoading = true | |
| getAPI('listSupportedNetworkServices', {}).then(json => { | |
| this.supportedServices = json.listsupportednetworkservicesresponse.networkservice || [] | |
| for (const i in this.supportedServices) { | |
| const networkServiceObj = this.supportedServices[i] | |
| const serviceName = networkServiceObj.name | |
| const serviceDisplayName = serviceName | |
| const providers = [] | |
| for (const j in this.supportedServices[i].provider) { | |
| const provider = this.supportedServices[i].provider[j] | |
| providers.push({ | |
| name: provider.name, | |
| enabled: true | |
| }) | |
| } | |
| this.supportedServices[i].provider = providers | |
| this.supportedServices[i].description = serviceDisplayName | |
| } | |
| this.supportedSvcs = this.supportedServices.slice() | |
| this.populateFormFromResource() | |
| this.updateSupportedServices() | |
| }).finally(() => { | |
| this.supportedServiceLoading = false | |
| }) | |
| }, | |
| handleLbTypeChange (lbType) { | |
| this.lbType = lbType | |
| // Enable InternalLbVm provider for Lb service if internalLb is selected | |
| if (lbType === 'internalLb') { | |
| this.supportedServices = this.supportedServices.map(svc => { | |
| if (svc.name === 'Lb') { | |
| const providers = svc.provider.map(provider => { | |
| provider.enabled = provider.name === 'InternalLbVm' | |
| this.lbServiceProvider = provider | |
| return provider | |
| }) | |
| return { ...svc, provider: providers, defaultChecked: true, selectedProvider: 'InternalLbVm' } | |
| } | |
| return svc | |
| }) | |
| this.selectedServiceProviderMap.Lb = 'InternalLbVm' | |
| } else { | |
| // Revert to default providers for Lb service | |
| this.supportedServices = this.supportedServices.map(svc => { | |
| if (svc.name === 'Lb') { | |
| const providers = svc.provider.map(provider => { | |
| provider.enabled = provider.name !== 'InternalLbVm' | |
| return provider | |
| }) | |
| // Pick the first enabled provider as selected | |
| const firstEnabled = providers.find(p => p.enabled) | |
| return { ...svc, provider: providers, defaultChecked: !!firstEnabled, selectedProvider: firstEnabled ? firstEnabled.name : null } | |
| } | |
| return svc | |
| }) | |
| // Update selectedServiceProviderMap for Lb | |
| const lbSvc = this.supportedServices.find(svc => svc.name === 'Lb') | |
| if (lbSvc && lbSvc.selectedProvider) { | |
| this.selectedServiceProviderMap.Lb = lbSvc.selectedProvider | |
| } else { | |
| delete this.selectedServiceProviderMap.Lb | |
| } | |
| } | |
| this.updateSupportedServices() | |
| }, | |
| populateFormFromResource () { | |
| if (!this.resource) return | |
| const r = this.resource | |
| this.form.name = r.name + ' - Clone' | |
| this.form.displaytext = r.displaytext || r.name | |
| if (r.guestiptype) { | |
| this.guestType = r.guestiptype.toLowerCase() | |
| this.form.guestiptype = r.guestiptype.toLowerCase() | |
| if (this.guestType === 'l2' && r.service && Array.isArray(r.service)) { | |
| this.form.userdatal2 = r.service.some(svc => svc.name === 'UserData') | |
| } | |
| } | |
| if (r.internetprotocol) { | |
| this.form.internetprotocol = r.internetprotocol.toLowerCase() | |
| this.internetProtocolValue = r.internetprotocol.toLowerCase() | |
| } | |
| if (r.forvpc !== undefined) { | |
| this.forVpc = r.forvpc | |
| this.form.forvpc = r.forvpc | |
| } | |
| if (r.service && Array.isArray(r.service)) { | |
| // Prefer provider from NetworkACL service if present (for NSX/Netris), else scan other services | |
| let detectedProvider = null | |
| const networkAclService = r.service.find(svc => svc.name === 'NetworkACL') | |
| if (networkAclService && networkAclService.provider && networkAclService.provider.length > 0) { | |
| const provName = (networkAclService.provider[0].name || '').toLowerCase() | |
| if (provName === 'nsx') detectedProvider = 'NSX' | |
| else if (provName === 'netris') detectedProvider = 'Netris' | |
| } | |
| if (!detectedProvider) { | |
| // fallback: detect provider (Nsx/Netris) from any service provider in the source offering | |
| for (const svc of r.service) { | |
| if (svc.provider && Array.isArray(svc.provider) && svc.provider.length > 0) { | |
| const provName = (svc.provider[0].name || '').toLowerCase() | |
| if (provName === 'nsx') { | |
| detectedProvider = 'NSX' | |
| break | |
| } else if (provName === 'netris') { | |
| detectedProvider = 'Netris' | |
| break | |
| } | |
| } | |
| } | |
| } | |
| if (detectedProvider) { | |
| this.provider = detectedProvider | |
| this.form.provider = detectedProvider | |
| if (detectedProvider === 'NSX') { | |
| this.nsxSupportedServicesMap = {} | |
| for (const svc of r.service) { | |
| if (svc.provider && svc.provider.length > 0) { | |
| const provName = svc.provider[0].name | |
| if (provName && provName.toLowerCase() === 'nsx') { | |
| this.nsxSupportedServicesMap[svc.name] = this.NSX | |
| } else if (svc.name === 'Dhcp' || svc.name === 'Dns' || svc.name === 'UserData') { | |
| this.nsxSupportedServicesMap[svc.name] = this.forVpc ? this.VPCVR : this.VR | |
| } | |
| } | |
| } | |
| if (this.forVpc) { | |
| this.nsxSupportedServicesMap.NetworkACL = this.NSX | |
| } else { | |
| this.nsxSupportedServicesMap.Firewall = this.NSX | |
| } | |
| } | |
| } | |
| } | |
| if (r.networkmode) { | |
| this.networkmode = r.networkmode | |
| this.form.networkmode = r.networkmode | |
| } | |
| if (r.supportsinternallb) { | |
| this.form.lbtype = 'internalLb' | |
| } | |
| if (r.availability) { | |
| const isIsolatedWithSourceNat = this.guestType === 'isolated' && | |
| r.service && | |
| r.service.some(svc => svc.name === 'SourceNat') | |
| if (isIsolatedWithSourceNat && this.requiredNetworkOfferingExists) { | |
| this.form.availability = 'Optional' | |
| } else { | |
| this.form.availability = r.availability | |
| } | |
| } | |
| if (r.tags) this.form.tags = r.tags | |
| if (r.conservemode !== undefined) this.form.conservemode = r.conservemode | |
| if (r.ispersistent !== undefined) this.form.ispersistent = r.ispersistent | |
| if (r.specifyvlan !== undefined) this.form.specifyvlan = r.specifyvlan | |
| if (r.specifyipranges !== undefined) this.form.specifyipranges = r.specifyipranges | |
| if (r.egressdefaultpolicy !== undefined) { | |
| this.form.egressdefaultpolicy = r.egressdefaultpolicy ? 'allow' : 'deny' | |
| } | |
| if (r.networkrate) this.form.networkrate = r.networkrate | |
| if (r.serviceofferingid) this.form.serviceofferingid = r.serviceofferingid | |
| if (r.details) { | |
| if (r.details.promiscuousmode) this.form.promiscuousmode = r.details.promiscuousmode | |
| if (r.details.macaddresschanges) this.form.macaddresschanges = r.details.macaddresschanges | |
| if (r.details.forgedtransmits) this.form.forgedtransmits = r.details.forgedtransmits | |
| if (r.details.maclearning) this.form.maclearning = r.details.maclearning | |
| } | |
| this.forVpc = r.forvpc || false | |
| if (r.service && Array.isArray(r.service)) { | |
| const sourceServiceMap = {} | |
| r.service.forEach(svc => { | |
| if (svc.provider && svc.provider.length > 0) { | |
| const providerName = svc.provider[0].name | |
| sourceServiceMap[svc.name] = providerName | |
| } | |
| }) | |
| this.serviceProviderMap = sourceServiceMap | |
| const updatedServices = this.supportedServices.map(svc => { | |
| const serviceCopy = { ...svc, provider: [...svc.provider] } | |
| if (sourceServiceMap[serviceCopy.name]) { | |
| const providerName = sourceServiceMap[serviceCopy.name] | |
| const providerIndex = serviceCopy.provider.findIndex(p => p.name === providerName) | |
| if (providerIndex > 0) { | |
| const targetProvider = serviceCopy.provider[providerIndex] | |
| serviceCopy.provider.splice(providerIndex, 1) | |
| serviceCopy.provider.unshift(targetProvider) | |
| } | |
| serviceCopy.defaultChecked = true | |
| serviceCopy.selectedProvider = providerName | |
| this.selectedServiceProviderMap[serviceCopy.name] = providerName | |
| } else { | |
| serviceCopy.defaultChecked = false | |
| serviceCopy.selectedProvider = null | |
| } | |
| return serviceCopy | |
| }) | |
| this.supportedServices = updatedServices | |
| this.supportedSvcs = updatedServices.map(svc => ({ ...svc })) | |
| this.sourceNatServiceChecked = !!this.selectedServiceProviderMap.SourceNat | |
| this.lbServiceChecked = !!this.selectedServiceProviderMap.Lb | |
| this.lbServiceProvider = this.selectedServiceProviderMap.Lb || '' | |
| this.firewallServiceChecked = !!this.selectedServiceProviderMap.Firewall | |
| this.firewallServiceProvider = this.selectedServiceProviderMap.Firewall || '' | |
| this.staticNatServiceChecked = !!this.selectedServiceProviderMap.StaticNat | |
| this.staticNatServiceProvider = this.selectedServiceProviderMap.StaticNat || '' | |
| this.connectivityServiceChecked = !!this.selectedServiceProviderMap.Connectivity | |
| this.$nextTick(() => { | |
| this.servicesReady = true | |
| this.$nextTick(() => { | |
| this.checkVirtualRouterForServices() | |
| }) | |
| }) | |
| } | |
| if (this.provider === 'NSX') { | |
| this.form.nsxsupportlb = Boolean(this.serviceProviderMap.Lb) | |
| this.form.nsxsupportsinternallb = Boolean(r.nsxsupportsinternallb) | |
| this.handleNsxLbService(this.form.nsxsupportlb) | |
| } | |
| }, | |
| updateSupportedServices () { | |
| const supportedServices = this.supportedServices || [] | |
| if (!this.supportedSvcs || this.supportedSvcs.length === 0) { | |
| this.supportedSvcs = supportedServices.slice() | |
| } | |
| if (this.provider !== 'NSX' && this.provider !== 'Netris') { | |
| const filtered = supportedServices.map(svc => { | |
| if (svc.name !== 'Connectivity') { | |
| const providers = svc.provider.map(provider => { | |
| if (this.forVpc) { | |
| const enabledProviders = ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive'] | |
| if (self.lbType === 'internalLb') { | |
| enabledProviders.push('InternalLbVm') | |
| } | |
| provider.enabled = enabledProviders.includes(provider.name) | |
| } else { | |
| provider.enabled = !['InternalLbVm', 'VpcVirtualRouter', 'Nsx', 'Netris'].includes(provider.name) | |
| } | |
| return provider | |
| }) | |
| return { ...svc, provider: providers } | |
| } | |
| return svc | |
| }) | |
| this.supportedServices = filtered | |
| } else { | |
| let svcMap = this.provider === 'NSX' ? this.nsxSupportedServicesMap : this.netrisSupportedServicesMap | |
| if (!svcMap || Object.keys(svcMap).length === 0) { | |
| svcMap = {} | |
| const forVpc = this.forVpc | |
| const baseProvider = this.provider === 'NSX' ? this.NSX : this.Netris | |
| const vrProvider = forVpc ? this.VPCVR : this.VR | |
| svcMap.Dhcp = vrProvider | |
| svcMap.Dns = vrProvider | |
| svcMap.UserData = vrProvider | |
| if (this.networkmode === 'NATTED') { | |
| svcMap.SourceNat = baseProvider | |
| svcMap.StaticNat = baseProvider | |
| svcMap.PortForwarding = baseProvider | |
| svcMap.Lb = baseProvider | |
| } | |
| if (forVpc) { | |
| svcMap.NetworkACL = baseProvider | |
| } else { | |
| svcMap.Firewall = baseProvider | |
| } | |
| } | |
| const filtered = this.supportedSvcs.filter(svc => Object.keys(svcMap).includes(svc.name)) | |
| .map(svc => { | |
| const preserveSelected = svc.selectedProvider !== undefined ? { defaultChecked: svc.defaultChecked, selectedProvider: svc.selectedProvider } : {} | |
| const mappedProvider = svcMap[svc.name] | |
| if (!['Dhcp', 'Dns', 'UserData'].includes(svc.name)) { | |
| return { ...svc, provider: [mappedProvider], ...preserveSelected } | |
| } | |
| return { ...svc, provider: [mappedProvider], ...preserveSelected } | |
| }) | |
| this.supportedServices = filtered | |
| const newSelectedMap = {} | |
| for (const svc of filtered) { | |
| const mappedProvider = svc.provider && svc.provider[0] | |
| const providerName = svc.selectedProvider || (mappedProvider && mappedProvider.name) || null | |
| if (providerName) { | |
| newSelectedMap[svc.name] = providerName | |
| } | |
| } | |
| this.selectedServiceProviderMap = newSelectedMap | |
| } | |
| }, | |
| checkVirtualRouterForServices () { | |
| this.isVirtualRouterForAtLeastOneService = false | |
| this.isVpcVirtualRouterForAtLeastOneService = false | |
| for (const service in this.selectedServiceProviderMap) { | |
| const provider = this.selectedServiceProviderMap[service] | |
| if (provider === 'VirtualRouter') { | |
| this.isVirtualRouterForAtLeastOneService = true | |
| } else if (provider === 'VpcVirtualRouter') { | |
| this.isVirtualRouterForAtLeastOneService = true | |
| this.isVpcVirtualRouterForAtLeastOneService = true | |
| } | |
| } | |
| }, | |
| handleGuestTypeChange (val) { | |
| this.guestType = val | |
| }, | |
| handleForVpcChange (val) { | |
| this.forVpc = val | |
| this.updateSupportedServices() | |
| }, | |
| handleNsxLbService (supportLb) { | |
| const forVpc = this.forVpc | |
| const baseProvider = this.NSX | |
| const vrProvider = forVpc ? this.VPCVR : this.VR | |
| const map = { | |
| Dhcp: vrProvider, | |
| Dns: vrProvider, | |
| UserData: vrProvider | |
| } | |
| const wantSourceNat = this.networkmode === 'NATTED' || !!this.serviceProviderMap.SourceNat | |
| const wantStaticNat = this.networkmode === 'NATTED' || !!this.serviceProviderMap.StaticNat | |
| const wantPortForwarding = this.networkmode === 'NATTED' || !!this.serviceProviderMap.PortForwarding | |
| const wantLb = supportLb || !!this.serviceProviderMap.Lb | |
| if (wantSourceNat) map.SourceNat = baseProvider | |
| if (wantStaticNat) map.StaticNat = baseProvider | |
| if (wantPortForwarding) map.PortForwarding = baseProvider | |
| if (wantLb) map.Lb = baseProvider | |
| if (forVpc) map.NetworkACL = baseProvider | |
| else map.Firewall = baseProvider | |
| this.nsxSupportedServicesMap = map | |
| this.updateSupportedServices() | |
| }, | |
| handleForNetworkModeChange (val) { | |
| this.networkmode = val | |
| const routedExcludedServices = ['SourceNat', 'StaticNat', 'Lb', 'PortForwarding', 'Vpn'] | |
| if (val === 'ROUTED' && this.guestType === 'isolated') { | |
| routedExcludedServices.forEach(service => { | |
| if (this.selectedServiceProviderMap[service]) { | |
| this.handleSupportedServiceChange(service, false, null) | |
| } | |
| }) | |
| this.supportedServices = this.supportedServices.map(svc => { | |
| if (routedExcludedServices.includes(svc.name)) { | |
| return { | |
| ...svc, | |
| defaultChecked: false, | |
| selectedProvider: null | |
| } | |
| } | |
| return svc | |
| }) | |
| } else if (val === 'NATTED') { | |
| routedExcludedServices.forEach(service => { | |
| if (!this.selectedServiceProviderMap[service] && this.serviceProviderMap[service]) { | |
| const provider = this.serviceProviderMap[service] | |
| this.handleSupportedServiceChange(service, true, provider) | |
| } | |
| }) | |
| this.supportedServices = this.supportedServices.map(svc => { | |
| if (routedExcludedServices.includes(svc.name) && this.serviceProviderMap[svc.name]) { | |
| return { | |
| ...svc, | |
| defaultChecked: true, | |
| selectedProvider: this.serviceProviderMap[svc.name] | |
| } | |
| } | |
| return svc | |
| }) | |
| } | |
| }, | |
| isAdmin () { | |
| return isAdmin() | |
| }, | |
| fetchDomainData () { | |
| const params = {} | |
| params.listAll = true | |
| params.showicon = true | |
| params.details = 'min' | |
| this.domainLoading = true | |
| getAPI('listDomains', params).then(json => { | |
| const listDomains = json.listdomainsresponse.domain | |
| this.domains = this.domains.concat(listDomains) | |
| }).finally(() => { | |
| this.domainLoading = false | |
| }) | |
| }, | |
| fetchZoneData () { | |
| const params = {} | |
| params.showicon = true | |
| this.zoneLoading = true | |
| getAPI('listZones', params).then(json => { | |
| const listZones = json.listzonesresponse.zone | |
| if (listZones) { | |
| this.zones = this.zones.concat(listZones) | |
| } | |
| }).finally(() => { | |
| this.zoneLoading = false | |
| }) | |
| }, | |
| checkRequiredNetworkOfferingExists () { | |
| return getAPI('listNetworkOfferings', { | |
| guestiptype: 'Isolated', | |
| sourcenatsupported: true, | |
| availability: 'Required', | |
| state: 'Enabled' | |
| }).then(json => { | |
| const offerings = json.listnetworkofferingsresponse.networkoffering || [] | |
| this.requiredNetworkOfferingExists = offerings.length > 0 | |
| return this.requiredNetworkOfferingExists | |
| }).catch(error => { | |
| console.error('checkRequiredNetworkOfferingExists: Error checking for required offerings:', error) | |
| this.requiredNetworkOfferingExists = false | |
| return false | |
| }) | |
| }, | |
| fetchIpv6NetworkOfferingConfiguration () { | |
| this.ipv6NetworkOfferingEnabled = false | |
| const params = { name: 'ipv6.offering.enabled' } | |
| getAPI('listConfigurations', params).then(json => { | |
| const value = json?.listconfigurationsresponse?.configuration?.[0].value || null | |
| this.ipv6NetworkOfferingEnabled = value === 'true' | |
| }) | |
| }, | |
| fetchRegisteredServicePackages () { | |
| this.registeredServicePackageLoading = true | |
| getAPI('listRegisteredServicePackages', {}).then(json => { | |
| this.registeredServicePackages = json.listregisteredservicepackage?.registeredServicePackage || [] | |
| }).finally(() => { | |
| this.registeredServicePackageLoading = false | |
| }) | |
| }, | |
| handleSupportedServiceChange (service, checked, provider) { | |
| if (checked) { | |
| const correctProvider = this.serviceProviderMap[service] | |
| if (correctProvider && provider !== correctProvider) { | |
| this.selectedServiceProviderMap[service] = correctProvider | |
| } else { | |
| this.selectedServiceProviderMap[service] = provider | |
| } | |
| } else { | |
| delete this.selectedServiceProviderMap[service] | |
| } | |
| if (service === 'SourceNat') { | |
| this.sourceNatServiceChecked = checked | |
| } else if (service === 'Lb') { | |
| this.lbServiceChecked = checked | |
| this.lbServiceProvider = checked ? provider : '' | |
| if (checked && provider === 'Netscaler') { | |
| this.fetchRegisteredServicePackages() | |
| } | |
| } else if (service === 'Firewall') { | |
| this.firewallServiceChecked = checked | |
| this.firewallServiceProvider = checked ? provider : '' | |
| } else if (service === 'StaticNat') { | |
| this.staticNatServiceChecked = checked | |
| this.staticNatServiceProvider = checked ? provider : '' | |
| } else if (service === 'Connectivity') { | |
| this.connectivityServiceChecked = checked | |
| } | |
| this.checkVirtualRouterForServices() | |
| }, | |
| handleSubmit (e) { | |
| if (e) { | |
| e.preventDefault() | |
| } | |
| if (this.loading) return | |
| this.formRef.value.validate().then(() => { | |
| const formRaw = toRaw(this.form) | |
| const values = this.handleRemoveFields(formRaw) | |
| const params = { | |
| sourceofferingid: this.resource.id, | |
| name: values.name | |
| } | |
| if (values.displaytext) { | |
| params.displaytext = values.displaytext | |
| } | |
| const forNsx = values.provider === 'NSX' | |
| if (forNsx) { | |
| params.provider = 'NSX' | |
| params.nsxsupportlb = values.nsxsupportlb | |
| if ('nsxsupportsinternallb' in values) { | |
| params.nsxsupportsinternallb = values.nsxsupportsinternallb | |
| } | |
| } | |
| const forNetris = values.provider === 'Netris' | |
| if (forNetris) { | |
| params.provider = 'Netris' | |
| } | |
| if (values.guestiptype) { | |
| params.guestiptype = values.guestiptype | |
| } | |
| // Use composable for service capability params | |
| if (this.selectedServiceProviderMap != null) { | |
| buildServiceCapabilityParams(params, values, this.selectedServiceProviderMap, this.registeredServicePackages) | |
| } else { | |
| if (!('supportedservices' in params)) { | |
| params.supportedservices = '' | |
| } | |
| } | |
| if (values.guestiptype === 'l2' && values.userdatal2 === true) { | |
| params.supportedservices = 'UserData' | |
| } | |
| if (values.availability) { | |
| params.availability = values.availability | |
| } | |
| if (values.tags) { | |
| params.tags = values.tags | |
| } | |
| if (values.specifyvlan !== undefined) { | |
| params.specifyvlan = values.specifyvlan | |
| } | |
| if (values.conservemode !== undefined) { | |
| params.conservemode = values.conservemode | |
| } | |
| if (values.ispersistent !== undefined) { | |
| params.ispersistent = values.ispersistent | |
| } | |
| if (values.specifyipranges !== undefined) { | |
| params.specifyipranges = values.specifyipranges | |
| } | |
| if (values.egressdefaultpolicy !== undefined) { | |
| params.egressdefaultpolicy = values.egressdefaultpolicy | |
| } | |
| if (values.networkrate) { | |
| params.networkrate = values.networkrate | |
| } | |
| if (values.serviceofferingid) { | |
| params.serviceofferingid = values.serviceofferingid | |
| } | |
| if (values.enable !== undefined) { | |
| params.enable = values.enable | |
| } | |
| if (values.ispublic !== true) { | |
| const domainIndexes = values.domainid | |
| let domainId = null | |
| if (domainIndexes && domainIndexes.length > 0) { | |
| const domainIds = [] | |
| for (let i = 0; i < domainIndexes.length; i++) { | |
| domainIds.push(this.domains[domainIndexes[i]].id) | |
| } | |
| domainId = domainIds.join(',') | |
| } | |
| if (domainId) { | |
| params.domainid = domainId | |
| } | |
| } | |
| if (values.networkmode) { | |
| params.networkmode = values.networkmode | |
| } | |
| const zoneIndexes = values.zoneid | |
| let zoneId = null | |
| if (zoneIndexes && zoneIndexes.length > 0) { | |
| const zoneIds = [] | |
| for (let j = 0; j < zoneIndexes.length; j++) { | |
| zoneIds.push(this.zones[zoneIndexes[j]].id) | |
| } | |
| zoneId = zoneIds.join(',') | |
| } | |
| if (zoneId) { | |
| params.zoneid = zoneId | |
| } | |
| params.traffictype = this.resource.traffictype || 'GUEST' | |
| let detailsIndex = 0 | |
| if (values.promiscuousmode && values.promiscuousmode !== '') { | |
| params['details[' + detailsIndex + '].key'] = 'promiscuousmode' | |
| params['details[' + detailsIndex + '].value'] = values.promiscuousmode | |
| detailsIndex++ | |
| } | |
| if (values.macaddresschanges && values.macaddresschanges !== '') { | |
| params['details[' + detailsIndex + '].key'] = 'macaddresschanges' | |
| params['details[' + detailsIndex + '].value'] = values.macaddresschanges | |
| detailsIndex++ | |
| } | |
| if (values.forgedtransmits && values.forgedtransmits !== '') { | |
| params['details[' + detailsIndex + '].key'] = 'forgedtransmits' | |
| params['details[' + detailsIndex + '].value'] = values.forgedtransmits | |
| detailsIndex++ | |
| } | |
| if (values.maclearning && values.maclearning !== '') { | |
| params['details[' + detailsIndex + '].key'] = 'maclearning' | |
| params['details[' + detailsIndex + '].value'] = values.maclearning | |
| detailsIndex++ | |
| } | |
| this.loading = true | |
| postAPI('cloneNetworkOffering', params).then(json => { | |
| this.$message.success(`${this.$t('message.success.clone.network.offering')} ${values.name}`) | |
| this.$emit('refresh-data') | |
| this.closeAction() | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| }).catch(error => { | |
| this.formRef.value.scrollToField(error.errorFields[0].name) | |
| }) | |
| }, | |
| closeAction () { | |
| this.$emit('close-action') | |
| } | |
| } | |
| } | |
| </script> | |
| <style scoped lang="less"> | |
| .form-layout { | |
| width: 80vw; | |
| @media (min-width: 700px) { | |
| width: 550px; | |
| } | |
| } | |
| .supported-services-container { | |
| height: 250px; | |
| overflow-y: scroll; | |
| } | |
| </style> |