| // 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> | |
| <div @keyup.ctrl.enter="handleOpenAddVMModal"> | |
| <div class="form"> | |
| <div class="form__item" ref="newRuleName"> | |
| <div class="form__label"><span class="form__required">*</span>{{ $t('label.name') }}</div> | |
| <a-input v-focus="true" v-model:value="newRule.name"></a-input> | |
| <span class="error-text">{{ $t('label.required') }}</span> | |
| </div> | |
| <div class="form__item" ref="newRulePublicPort"> | |
| <div class="form__label"><span class="form__required">*</span>{{ $t('label.publicport') }}</div> | |
| <a-input v-model:value="newRule.publicport"></a-input> | |
| <span class="error-text">{{ $t('label.required') }}</span> | |
| </div> | |
| <div class="form__item" ref="newRulePrivatePort"> | |
| <div class="form__label"><span class="form__required">*</span>{{ $t('label.privateport') }}</div> | |
| <a-input v-model:value="newRule.privateport"></a-input> | |
| <span class="error-text">{{ $t('label.required') }}</span> | |
| </div> | |
| </div> | |
| <div class="form"> | |
| <div class="form__item" ref="newCidrList"> | |
| <tooltip-label :title="$t('label.sourcecidrlist')" bold :tooltip="createLoadBalancerRuleParams.cidrlist.description" :tooltip-placement="'right'"/> | |
| <a-input v-model:value="newRule.cidrlist"></a-input> | |
| </div> | |
| <div class="form__item" v-if="lbProvider !== 'Netris'"> | |
| <div class="form__label">{{ $t('label.algorithm') }}</div> | |
| <a-select | |
| v-model:value="newRule.algorithm" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option value="roundrobin" :label="$t('label.lb.algorithm.roundrobin')">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option> | |
| <a-select-option value="leastconn" :label="$t('label.lb.algorithm.leastconn')">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option> | |
| <a-select-option value="source" :label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') }}</a-select-option> | |
| </a-select> | |
| </div> | |
| <div class="form__item"> | |
| <div class="form__label">{{ $t('label.protocol') }}</div> | |
| <a-select | |
| v-model:value="newRule.protocol" | |
| style="min-width: 100px" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option v-if="lbProvider !== 'Netris'" value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option> | |
| <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option> | |
| <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option> | |
| <a-select-option value="ssl" :label="$t('label.ssl')">{{ $t('label.ssl') }}</a-select-option> | |
| </a-select> | |
| </div> | |
| <div class="form__item"> | |
| <div class="form__label">{{ $t('label.autoscale') }}</div> | |
| <a-select | |
| v-model:value="newRule.autoscale" | |
| defaultValue="no" | |
| style="min-width: 100px" | |
| showSearch | |
| optionFilterProp="value" | |
| :filterOption="(input, option) => { | |
| return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option value="yes">{{ $t('label.yes') }}</a-select-option> | |
| <a-select-option value="no">{{ $t('label.no') }}</a-select-option> | |
| </a-select> | |
| </div> | |
| <div class="form__item" v-if="newRule.protocol === 'ssl'" > | |
| <div class="form__label">{{ $t('label.sslcertificate') }}</div> | |
| <a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddSslCertModal(null)"> | |
| {{ this.selectedSsl.id != null ? this.selectedSsl.name : $t('label.add') }} | |
| </a-button> | |
| </div> | |
| <div class="form__item" v-if="!newRule.autoscale || newRule.autoscale === 'no'"> | |
| <div class="form__label" style="white-space: nowrap;">{{ $t('label.add.vms') }}</div> | |
| <a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddVMModal"> | |
| {{ $t('label.add') }} | |
| </a-button> | |
| </div> | |
| <div class="form__item" v-else-if="newRule.autoscale === 'yes' && ('vpcid' in this.resource && !this.associatednetworkid)"> | |
| <div class="form__label" style="white-space: nowrap;">{{ $t('label.select.tier') }}</div> | |
| <a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleOpenAddNetworkModal"> | |
| {{ $t('label.add') }} | |
| </a-button> | |
| </div> | |
| <div class="form__item" v-else-if="newRule.autoscale === 'yes'"> | |
| <div class="form__label" style="white-space: nowrap;">{{ $t('label.add') }}</div> | |
| <a-button :disabled="!('createLoadBalancerRule' in $store.getters.apis)" type="primary" @click="handleAddNewRule"> | |
| {{ $t('label.add') }} | |
| </a-button> | |
| </div> | |
| </div> | |
| </div> | |
| <a-divider /> | |
| <a-button | |
| v-if="(('deleteLoadBalancerRule' in $store.getters.apis) && this.selectedItems.length > 0)" | |
| type="primary" | |
| danger | |
| style="width: 100%; margin-bottom: 15px" | |
| @click="bulkActionConfirmation()"> | |
| <template #icon><delete-outlined /></template> | |
| {{ $t('label.action.bulk.delete.load.balancer.rules') }} | |
| </a-button> | |
| <a-table | |
| size="small" | |
| class="list-view" | |
| :loading="loading" | |
| :columns="columns" | |
| :dataSource="lbRules" | |
| :pagination="false" | |
| :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}" | |
| :rowKey="record => record.id"> | |
| <template #bodyCell="{ column, record }"> | |
| <template v-if="column.key === 'cidrlist'"> | |
| <span style="white-space: pre-line"> {{ record.cidrlist?.replaceAll(",", "\n") }}</span> | |
| </template> | |
| <template v-if="column.key === 'algorithm'"> | |
| {{ returnAlgorithmName(record.algorithm) }} | |
| </template> | |
| <template v-if="column.key === 'protocol'"> | |
| {{ getCapitalise(record.protocol) }} | |
| </template> | |
| <template v-if="column.key === 'stickiness' && !this.isNetrisZone"> | |
| <a-button @click="() => openStickinessModal(record.id)"> | |
| {{ returnStickinessLabel(record.id) }} | |
| </a-button> | |
| </template> | |
| <template v-if="column.key === 'sslcert'"> | |
| <a-button :disabled="record.protocol !== 'ssl'" @click="() => { selectedRule = record; handleOpenAddSslCertModal(record) }"> | |
| <template #icon><plus-outlined /></template> | |
| {{ $t('label.manage') }} | |
| </a-button> | |
| </template> | |
| <template v-if="column.key === 'autoscale'"> | |
| <div> | |
| <router-link :to="{ path: '/autoscalevmgroup/' + record.autoscalevmgroup.id }" v-if='record.autoscalevmgroup'> | |
| <a-button>{{ $t('label.view') }}</a-button> | |
| </router-link> | |
| <router-link :to="{ path: '/action/createAutoScaleVmGroup', query: { networkid: record.networkid, lbruleid : record.id } }" v-else-if='!record.ruleInstances'> | |
| <a-button>{{ $t('label.new') }}</a-button> | |
| </router-link> | |
| </div> | |
| </template> | |
| <template v-if="column.key === 'healthmonitor'"> | |
| <a-button @click="() => openHealthMonitorModal(record.id)"> | |
| {{ returnHealthMonitorLabel(record.id) }} | |
| </a-button> | |
| </template> | |
| <template v-if="column.key === 'add'"> | |
| <a-button v-if="!record.autoscalevmgroup" type="primary" @click="() => { selectedRule = record; handleOpenAddVMModal() }"> | |
| <template #icon><plus-outlined /></template> | |
| {{ $t('label.add') }} | |
| </a-button> | |
| </template> | |
| <template v-if="column.key === 'actions'"> | |
| <div class="actions"> | |
| <tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(record)" /> | |
| <tooltip-button :tooltip="$t('label.edit.tags')" :disabled="!('updateLoadBalancerRule' in $store.getters.apis)" icon="tag-outlined" @onClick="() => openTagsModal(record.id)" /> | |
| <a-popconfirm | |
| :title="$t('label.delete') + '?'" | |
| @confirm="handleDeleteRule(record)" | |
| :okText="$t('label.yes')" | |
| :cancelText="$t('label.no')" | |
| > | |
| <tooltip-button | |
| :tooltip="$t('label.delete')" | |
| :disabled="!('deleteLoadBalancerRule' in $store.getters.apis)" | |
| type="primary" | |
| :danger="true" | |
| icon="delete-outlined" /> | |
| </a-popconfirm> | |
| </div> | |
| </template> | |
| </template> | |
| <template #expandedRowRender="{ record }"> | |
| <div class="rule-instance-list"> | |
| <div v-for="instance in record.ruleInstances" :key="instance.loadbalancerruleinstance.id"> | |
| <div v-for="ip in instance.lbvmipaddresses" :key="ip" class="rule-instance-list__item"> | |
| <div> | |
| <status :text="instance.loadbalancerruleinstance.state" /> | |
| <desktop-outlined /> | |
| <router-link :to="{ path: '/vm/' + instance.loadbalancerruleinstance.id }"> | |
| {{ instance.loadbalancerruleinstance.displayname }} | |
| </router-link> | |
| </div> | |
| <div>{{ ip }}</div> | |
| <tooltip-button | |
| :disabled='record.autoscalevmgroup' | |
| :tooltip="$t('label.remove.vm.from.lb')" | |
| type="primary" | |
| :danger="true" | |
| icon="delete-outlined" | |
| @onClick="() => handleDeleteInstanceFromRule(instance, record, ip)" /> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| </a-table> | |
| <a-pagination | |
| class="pagination" | |
| size="small" | |
| :current="page" | |
| :pageSize="pageSize" | |
| :total="totalCount" | |
| :showTotal="total => `${$t('label.total')} ${total} ${$t('label.items')}`" | |
| :pageSizeOptions="['10', '20', '40', '80', '100']" | |
| @change="handleChangePage" | |
| @showSizeChange="handleChangePageSize" | |
| showSizeChanger> | |
| <template #buildOptionText="props"> | |
| <span>{{ props.value }} / {{ $t('label.page') }}</span> | |
| </template> | |
| </a-pagination> | |
| <a-modal | |
| v-if="tagsModalVisible" | |
| :title="$t('label.edit.tags')" | |
| :visible="tagsModalVisible" | |
| :footer="null" | |
| :closable="true" | |
| :afterClose="closeModal" | |
| :maskClosable="false" | |
| class="tags-modal" | |
| @cancel="tagsModalVisible = false"> | |
| <span v-show="tagsModalLoading" class="modal-loading"> | |
| <loading-outlined /> | |
| </span> | |
| <a-form | |
| :ref="formRef" | |
| :model="form" | |
| :rules="rules" | |
| class="add-tags" | |
| @finish="handleAddTag" | |
| v-ctrl-enter="handleAddTag" | |
| > | |
| <div class="add-tags__input"> | |
| <p class="add-tags__label">{{ $t('label.key') }}</p> | |
| <a-form-item ref="key" name="key"> | |
| <a-input | |
| v-focus="true" | |
| v-model:value="form.key" /> | |
| </a-form-item> | |
| </div> | |
| <div class="add-tags__input"> | |
| <p class="add-tags__label">{{ $t('label.value') }}</p> | |
| <a-form-item ref="value" name="value"> | |
| <a-input v-model:value="form.value" /> | |
| </a-form-item> | |
| </div> | |
| <a-button :disabled="!('createTags' in $store.getters.apis)" type="primary" ref="submit" @click="handleAddTag">{{ $t('label.add') }}</a-button> | |
| </a-form> | |
| <a-divider /> | |
| <div v-show="!tagsModalLoading" class="tags-container"> | |
| <div class="tags" v-for="(tag, index) in tags" :key="index"> | |
| <a-tag :key="index" :closable="'deleteTags' in $store.getters.apis" @close="() => handleDeleteTag(tag)"> | |
| {{ tag.key }} = {{ tag.value }} | |
| </a-tag> | |
| </div> | |
| </div> | |
| <a-button class="add-tags-done" @click="tagsModalVisible = false" type="primary">{{ $t('label.done') }}</a-button> | |
| </a-modal> | |
| <a-modal | |
| :visible="stickinessModalVisible" | |
| :footer="null" | |
| :afterClose="closeModal" | |
| :maskClosable="false" | |
| :closable="true" | |
| :okButtonProps="{ props: {htmlType: 'submit'}}" | |
| @cancel="stickinessModalVisible = false"> | |
| <template #title> | |
| <span>{{ $t('label.configure.sticky.policy') }}</span> | |
| <a | |
| style="margin-left: 5px" | |
| :href="$config.docBase + '/adminguide/networking/external_firewalls_and_load_balancers.html#sticky-session-policies-for-load-balancer-rules'" | |
| target="_blank"> | |
| <question-circle-outlined /> | |
| </a> | |
| </template> | |
| <span v-show="stickinessModalLoading" class="modal-loading"> | |
| <loading-outlined /> | |
| </span> | |
| <a-form | |
| :ref="formRef" | |
| :model="form" | |
| :rules="rules" | |
| @finish="handleSubmitStickinessForm" | |
| v-ctrl-enter="handleSubmitStickinessForm" | |
| class="custom-ant-form" | |
| > | |
| <a-form-item name="methodname" ref="methodname"> | |
| <template #label> | |
| <tooltip-label :title="$t('label.stickiness.method')" :tooltip="createLoadBalancerStickinessPolicyParams.methodname.description" :tooltip-placement="'right'"/> | |
| </template> | |
| <a-select | |
| v-focus="true" | |
| v-model:value="form.methodname" | |
| @change="handleStickinessMethodSelectChange" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option value="LbCookie" :label="$t('label.lb.cookie')">{{ $t('label.lb.cookie') }}</a-select-option> | |
| <a-select-option value="AppCookie" :label="$t('label.app.cookie')">{{ $t('label.app.cookie') }}</a-select-option> | |
| <a-select-option value="SourceBased" :label="$t('label.source.based')">{{ $t('label.source.based') }}</a-select-option> | |
| <a-select-option value="none" :label="$t('label.none')">{{ $t('label.none') }}</a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item | |
| name="name" | |
| ref="name" | |
| v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod === | |
| 'AppCookie' || stickinessPolicyMethod === 'SourceBased'"> | |
| <a-input v-model:value="form.name" /> | |
| <template #label> | |
| <tooltip-label :title="$t('label.sticky.name')" :tooltip="createLoadBalancerStickinessPolicyParams.name.description" :tooltip-placement="'right'"/> | |
| </template> | |
| </a-form-item> | |
| <div v-if="stickinessPolicyMethod !== 'none'"> | |
| <br/> | |
| {{ $t('message.loadbalancer.stickypolicy.configuration') }} | |
| <br/> | |
| <a-card> | |
| <a-form-item | |
| name="cookieName" | |
| ref="cookieName" | |
| :label="$t('label.sticky.cookie-name')" | |
| v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod === | |
| 'AppCookie'"> | |
| <a-input v-model:value="form.cookieName" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="mode" | |
| ref="mode" | |
| :label="$t('label.sticky.mode')" | |
| v-show="stickinessPolicyMethod === 'LbCookie' || stickinessPolicyMethod === | |
| 'AppCookie'"> | |
| <a-input v-model:value="form.mode" /> | |
| </a-form-item> | |
| <a-form-item name="nocache" ref="nocache" :label="$t('label.sticky.nocache')" v-show="stickinessPolicyMethod === 'LbCookie'"> | |
| <a-checkbox v-model:checked="form.nocache"></a-checkbox> | |
| </a-form-item> | |
| <a-form-item name="indirect" ref="indirect" :label="$t('label.sticky.indirect')" v-show="stickinessPolicyMethod === 'LbCookie'"> | |
| <a-checkbox v-model:checked="form.indirect"></a-checkbox> | |
| </a-form-item> | |
| <a-form-item name="postonly" ref="postonly" :label="$t('label.sticky.postonly')" v-show="stickinessPolicyMethod === 'LbCookie'"> | |
| <a-checkbox v-model:checked="form.postonly"></a-checkbox> | |
| </a-form-item> | |
| <a-form-item name="domain" ref="domain" :label="$t('label.domain')" v-show="stickinessPolicyMethod === 'LbCookie'"> | |
| <a-input v-model:value="form.domain" /> | |
| </a-form-item> | |
| <a-form-item name="length" ref="length" :label="$t('label.sticky.length')" v-show="stickinessPolicyMethod === 'AppCookie'"> | |
| <a-input v-model:value="form.length" type="number" /> | |
| </a-form-item> | |
| <a-form-item name="holdtime" ref="holdtime" :label="$t('label.sticky.holdtime')" v-show="stickinessPolicyMethod === 'AppCookie'"> | |
| <a-input v-model:value="form.holdtime" type="number" /> | |
| </a-form-item> | |
| <a-form-item name="requestLearn" ref="requestLearn" :label="$t('label.sticky.request-learn')" v-show="stickinessPolicyMethod === 'AppCookie'"> | |
| <a-checkbox v-model:checked="form.requestLearn"></a-checkbox> | |
| </a-form-item> | |
| <a-form-item name="prefix" ref="prefix" :label="$t('label.sticky.prefix')" v-show="stickinessPolicyMethod === 'AppCookie'"> | |
| <a-checkbox v-model:checked="form.prefix"></a-checkbox> | |
| </a-form-item> | |
| <a-form-item name="tablesize" ref="tablesize" :label="$t('label.sticky.tablesize')" v-show="stickinessPolicyMethod === 'SourceBased'"> | |
| <a-input v-model:value="form.tablesize" /> | |
| </a-form-item> | |
| <a-form-item name="expire" ref="expire" :label="$t('label.sticky.expire')" v-show="stickinessPolicyMethod === 'SourceBased'"> | |
| <a-input v-model:value="form.expire" /> | |
| </a-form-item> | |
| </a-card> | |
| </div> | |
| <div :span="24" class="action-button"> | |
| <a-button @click="stickinessModalVisible = false">{{ $t('label.cancel') }}</a-button> | |
| <a-button type="primary" ref="submit" @click="handleSubmitStickinessForm">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </a-form> | |
| </a-modal> | |
| <a-modal | |
| :title="$t('label.edit.rule')" | |
| :visible="editRuleModalVisible" | |
| :afterClose="closeModal" | |
| :maskClosable="false" | |
| :closable="true" | |
| :footer="null" | |
| @cancel="editRuleModalVisible = false"> | |
| <span v-show="editRuleModalLoading" class="modal-loading"> | |
| <loading-outlined /> | |
| </span> | |
| <div class="edit-rule" v-if="selectedRule" v-ctrl-enter="handleSubmitEditForm"> | |
| <div class="edit-rule__item"> | |
| <p class="edit-rule__label">{{ $t('label.name') }}</p> | |
| <a-input v-focus="true" v-model:value="editRuleDetails.name" /> | |
| </div> | |
| <div v-if="lbProvider !== 'Netris'" class="edit-rule__item"> | |
| <p class="edit-rule__label">{{ $t('label.algorithm') }}</p> | |
| <a-select | |
| v-model:value="editRuleDetails.algorithm" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option value="roundrobin" :label="$t('label.lb.algorithm.roundrobin')">{{ $t('label.lb.algorithm.roundrobin') }}</a-select-option> | |
| <a-select-option value="leastconn" :label="$t('label.lb.algorithm.leastconn')">{{ $t('label.lb.algorithm.leastconn') }}</a-select-option> | |
| <a-select-option value="source" :label="$t('label.lb.algorithm.source')">{{ $t('label.lb.algorithm.source') }}</a-select-option> | |
| </a-select> | |
| </div> | |
| <div v-if="lbProvider !== 'Netris'" class="edit-rule__item"> | |
| <p class="edit-rule__label">{{ $t('label.protocol') }}</p> | |
| <a-select | |
| v-model:value="editRuleDetails.protocol" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option value="tcp-proxy" :label="$t('label.tcp.proxy')">{{ $t('label.tcp.proxy') }}</a-select-option> | |
| <a-select-option value="tcp" :label="$t('label.tcp')">{{ $t('label.tcp') }}</a-select-option> | |
| <a-select-option value="udp" :label="$t('label.udp')">{{ $t('label.udp') }}</a-select-option> | |
| <a-select-option value="ssl" :label="$t('label.ssl')">{{ $t('label.ssl') }}</a-select-option> | |
| </a-select> | |
| </div> | |
| <div v-if="lbProvider !== 'Netris'" class="edit-rule__item"> | |
| <p class="edit-rule__label"> | |
| {{ $t('label.sourcecidrlist') }} | |
| <tooltip-label | |
| :title="''" | |
| bold | |
| :tooltip="createLoadBalancerRuleParams.cidrlist.description || 'Enter a comma-separated list of CIDR blocks.'" | |
| :tooltip-placement="'right'" | |
| style="display: inline; margin-left: 5px;" | |
| /> | |
| </p> | |
| <a-input | |
| v-model:value="editRuleDetails.cidrlist" | |
| :placeholder="$t('label.sourcecidrlist')" | |
| /> | |
| </div> | |
| <div :span="24" class="action-button"> | |
| <a-button @click="() => editRuleModalVisible = false">{{ $t('label.cancel') }}</a-button> | |
| <a-button type="primary" @click="handleSubmitEditForm">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </div> | |
| </a-modal> | |
| <a-modal | |
| :title="$t('label.add.vms')" | |
| :maskClosable="false" | |
| :closable="true" | |
| v-if="addVmModalVisible" | |
| :visible="addVmModalVisible" | |
| class="vm-modal" | |
| width="60vw" | |
| :footer="null" | |
| @cancel="closeModal" | |
| > | |
| <div @keyup.ctrl.enter="handleAddNewRule"> | |
| <span | |
| v-if="'vpcid' in resource && !('associatednetworkid' in resource)"> | |
| <strong>{{ $t('label.select.tier') }} </strong> | |
| <a-select | |
| v-focus="'vpcid' in resource && !('associatednetworkid' in resource)" | |
| v-model:value="selectedTier" | |
| @change="fetchVirtualMachines()" | |
| :placeholder="$t('label.select.tier')" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option | |
| v-for="tier in tiers.data" | |
| :loading="tiers.loading" | |
| :key="tier.id" | |
| :label="tier.displaytext"> | |
| {{ tier.displaytext }} | |
| </a-select-option> | |
| </a-select> | |
| </span> | |
| <a-input-search | |
| v-focus="!('vpcid' in resource && !('associatednetworkid' in resource))" | |
| class="input-search" | |
| :placeholder="$t('label.search')" | |
| v-model:value="searchQuery" | |
| allowClear | |
| @search="onSearch" /> | |
| <a-table | |
| size="small" | |
| class="list-view" | |
| :loading="addVmModalLoading" | |
| :columns="vmColumns" | |
| :dataSource="vms" | |
| :pagination="false" | |
| :rowKey="record => record.id" | |
| :scroll="{ y: 300 }"> | |
| <template #bodyCell="{ column, text, record, index }"> | |
| <template v-if="column.key === 'name'"> | |
| <span> | |
| {{ text }} | |
| </span> | |
| <loading-outlined v-if="addVmModalNicLoading" /> | |
| <a-select | |
| style="display: block" | |
| v-else-if="!addVmModalNicLoading && newRule.virtualmachineid[index] === record.id" | |
| mode="multiple" | |
| v-model:value="newRule.vmguestip[index]" | |
| showSearch | |
| optionFilterProp="label" | |
| :filterOption="(input, option) => { | |
| return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }" > | |
| <a-select-option | |
| v-for="(nic, nicIndex) in nics[index]" | |
| :key="nic" | |
| :value="nic" | |
| :label="nic + nicIndex === 0 ? ` (${$t('label.primary')})` : null"> | |
| {{ nic }}{{ nicIndex === 0 ? ` (${$t('label.primary')})` : null }} | |
| </a-select-option> | |
| </a-select> | |
| </template> | |
| <template v-if="column.key === 'state'"> | |
| <status :text="text ? text : ''" displayText></status> | |
| </template> | |
| <template v-if="column.key === 'actions'" style="text-align: center" :text="text"> | |
| <a-checkbox v-model:value="record.id" @change="e => fetchNics(e, index)" /> | |
| </template> | |
| </template> | |
| </a-table> | |
| <a-pagination | |
| class="pagination" | |
| size="small" | |
| :current="vmPage" | |
| :pageSize="vmPageSize" | |
| :total="vmCount" | |
| :showTotal="total => `${$t('label.total')} ${total} ${$t('label.items')}`" | |
| :pageSizeOptions="['10', '20', '40', '80', '100']" | |
| @change="handleChangeVmPage" | |
| @showSizeChange="handleChangeVmPageSize" | |
| showSizeChanger> | |
| <template #buildOptionText="props"> | |
| <span>{{ props.value }} / {{ $t('label.page') }}</span> | |
| </template> | |
| </a-pagination> | |
| <div :span="24" class="action-button"> | |
| <a-button @click="closeModal">{{ $t('label.cancel') }}</a-button> | |
| <a-button :disabled="newRule.virtualmachineid === []" type="primary" ref="submit" @click="handleAddNewRule">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </div> | |
| </a-modal> | |
| <a-modal | |
| :title="$t('label.manage.ssl.cert')" | |
| :maskClosable="false" | |
| :closable="true" | |
| v-if="addSslCertModalVisible" | |
| :visible="addSslCertModalVisible" | |
| width="30vw" | |
| @cancel="addSslCertModalVisible = false" | |
| @ok="addSslCertModalVisible = false" | |
| :cancelButtonProps="{ style: { display: 'none' } }" | |
| > | |
| <a-row v-show="showAssignedSsl && assignedSslCert !== 'None'"> | |
| <a-col :span="8"> | |
| <div class="form__label">{{ $t("label.current") + ' ' + $t('label.sslcertificate') }}</div> | |
| </a-col> | |
| <a-col :span="10"> | |
| <div>{{ assignedSslCert }}</div> | |
| </a-col> | |
| <a-col :span="6"> | |
| <a-button :disabled="!deleteSslButtonVisible" type="danger" @click="removeSslFromLbRule()"> | |
| <template #icon><delete-outlined /></template> | |
| {{ $t('label.remove') }} | |
| </a-button> | |
| </a-col> | |
| </a-row> | |
| <a-row style="margin-top: 16px"> | |
| <a-col :span="8"> | |
| <div class="form__label">{{ $t("label.new") + ' ' + $t('label.sslcertificate') }}</div> | |
| </a-col> | |
| <a-col :span="10"> | |
| <div class="form__item"> | |
| <a-select v-model:value="selectedSsl.name" style="width: 80%;" @change="selectssl"> | |
| <a-select-option | |
| v-for="sslcert in sslcerts.data" | |
| :key="sslcert.id">{{ sslcert.name }} | |
| </a-select-option> | |
| </a-select> | |
| </div> | |
| </a-col> | |
| <a-col :span="6"> | |
| <div> | |
| <a-button v-show="addSslButtonVisible && assignedSslCert !== 'None'" type="primary" @click="addSslTolbRule()"> | |
| <template #icon><swap-outlined /></template> | |
| {{ $t('label.replace') }} | |
| </a-button> | |
| <a-button v-show="addSslButtonVisible && assignedSslCert === 'None'" type="primary" @click="addSslTolbRule()"> | |
| <template #icon><plus-outlined /></template> | |
| {{ $t('label.assign') }} | |
| </a-button> | |
| </div> | |
| </a-col> | |
| </a-row> | |
| </a-modal> | |
| <a-modal | |
| :title="$t('label.select.tier')" | |
| :maskClosable="false" | |
| :closable="true" | |
| v-if="addNetworkModalVisible" | |
| :visible="addNetworkModalVisible" | |
| class="network-modal" | |
| width="60vw" | |
| :footer="null" | |
| @cancel="closeModal" | |
| > | |
| <div @keyup.ctrl.enter="handleAddNewRule"> | |
| <a-input-search | |
| v-focus="!('vpcid' in resource && !('associatednetworkid' in resource))" | |
| class="input-search" | |
| :placeholder="$t('label.search')" | |
| v-model:value="searchQuery" | |
| allowClear | |
| @search="onNetworkSearch" /> | |
| <a-table | |
| size="small" | |
| class="list-view" | |
| :loading="addNetworkModalLoading" | |
| :columns="networkColumns" | |
| :dataSource="networks" | |
| :pagination="false" | |
| :rowKey="record => record.id" | |
| :scroll="{ y: 300 }"> | |
| <template #bodyCell="{ column, record }"> | |
| <template v-if="column.key === 'actions'"> | |
| <div style="text-align: center"> | |
| <a-radio-group | |
| class="radio-group" | |
| :key="record.id" | |
| v-model:value="this.selectedTierForAutoScaling" | |
| @change="($event) => this.selectedTierForAutoScaling = $event.target.value"> | |
| <a-radio :value="record.id" /> | |
| </a-radio-group> | |
| </div> | |
| </template> | |
| </template> | |
| </a-table> | |
| <a-pagination | |
| class="pagination" | |
| size="small" | |
| :current="networkPage" | |
| :pageSize="networkPageSize" | |
| :total="networkCount" | |
| :showTotal="total => `${$t('label.total')} ${total} ${$t('label.items')}`" | |
| :pageSizeOptions="['10', '20', '40', '80', '100']" | |
| @change="handleChangeNetworkPage" | |
| @showSizeChange="handleChangeNetworkPageSize" | |
| showSizeChanger> | |
| <template #buildOptionText="props"> | |
| <span>{{ props.value }} / {{ $t('label.page') }}</span> | |
| </template> | |
| </a-pagination> | |
| <div :span="24" class="action-button"> | |
| <a-button @click="closeModal">{{ $t('label.cancel') }}</a-button> | |
| <a-button :disabled="this.selectedTierForAutoScaling === null" type="primary" ref="submit" @click="handleAddNewRule">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </div> | |
| </a-modal> | |
| <a-modal | |
| v-if="healthMonitorModal" | |
| :title="$t('label.configure.health.monitor')" | |
| :visible="healthMonitorModal" | |
| :footer="null" | |
| :maskClosable="false" | |
| :closable="true" | |
| @cancel="closeMonitorModal"> | |
| <a-form | |
| :ref="monitorRef" | |
| :model="monitorForm" | |
| :rules="monitorRules" | |
| layout="vertical" | |
| @finish="handleConfigHealthMonitor" | |
| v-ctrl-enter="handleConfigHealthMonitor"> | |
| <a-form-item name="type" ref="type" :label="$t('label.monitor.type')"> | |
| <a-select | |
| v-focus="true" | |
| v-model:value="monitorForm.type" | |
| @change="(value) => { healthMonitorParams.type = value }" | |
| showSearch | |
| optionFilterProp="value" | |
| :filterOption="(input, option) => { | |
| return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }"> | |
| <a-select-option value="PING">PING</a-select-option> | |
| <a-select-option value="TCP">TCP</a-select-option> | |
| <a-select-option value="HTTP">HTTP</a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item name="retry" ref="retry" :label="$t('label.monitor.retry')"> | |
| <a-input v-model:value="monitorForm.retry" /> | |
| </a-form-item> | |
| <a-form-item name="timeout" ref="timeout" :label="$t('label.monitor.timeout')"> | |
| <a-input v-model:value="monitorForm.timeout" /> | |
| </a-form-item> | |
| <a-form-item name="interval" ref="interval" :label="$t('label.monitor.interval')"> | |
| <a-input v-model:value="monitorForm.interval" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="httpmethodtype" | |
| ref="httpmethodtype" | |
| :label="$t('label.monitor.http.method')" | |
| v-if="healthMonitorParams.type === 'HTTP'"> | |
| <a-select | |
| v-focus="true" | |
| v-model:value="monitorForm.httpmethodtype" | |
| showSearch | |
| optionFilterProp="value" | |
| :filterOption="(input, option) => { | |
| return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0 | |
| }"> | |
| <a-select-option value="GET">GET</a-select-option> | |
| <a-select-option value="HEAD">HEAD</a-select-option> | |
| </a-select> | |
| </a-form-item> | |
| <a-form-item | |
| name="expectedcode" | |
| ref="expectedcode" | |
| :label="$t('label.monitor.expected.code')" | |
| v-if="healthMonitorParams.type === 'HTTP'"> | |
| <a-input v-model:value="monitorForm.expectedcode" /> | |
| </a-form-item> | |
| <a-form-item | |
| name="urlpath" | |
| ref="urlpath" | |
| :label="$t('label.monitor.url')" | |
| v-if="healthMonitorParams.type === 'HTTP'"> | |
| <a-input v-model:value="monitorForm.urlpath" /> | |
| </a-form-item> | |
| <div :span="24" class="action-button"> | |
| <a-button :loading="healthMonitorLoading" @click="closeMonitorModal">{{ $t('label.cancel') }}</a-button> | |
| <a-button :loading="healthMonitorLoading" type="primary" @click="handleConfigHealthMonitor">{{ $t('label.ok') }}</a-button> | |
| </div> | |
| </a-form> | |
| </a-modal> | |
| <bulk-action-view | |
| v-if="showConfirmationAction || showGroupActionModal" | |
| :showConfirmationAction="showConfirmationAction" | |
| :showGroupActionModal="showGroupActionModal" | |
| :items="lbRules" | |
| :selectedRowKeys="selectedRowKeys" | |
| :selectedItems="selectedItems" | |
| :columns="columns" | |
| :selectedColumns="selectedColumns" | |
| :filterColumns="filterColumns" | |
| action="deleteLoadBalancerRule" | |
| :loading="loading" | |
| :message="message" | |
| @group-action="deleteRules" | |
| @handle-cancel="handleCancel" | |
| @close-modal="closeModal" /> | |
| </div> | |
| </template> | |
| <script> | |
| import { ref, reactive, toRaw, nextTick } from 'vue' | |
| import { getAPI, postAPI } from '@/api' | |
| import { mixinForm } from '@/utils/mixin' | |
| import Status from '@/components/widgets/Status' | |
| import TooltipButton from '@/components/widgets/TooltipButton' | |
| import BulkActionView from '@/components/view/BulkActionView' | |
| import eventBus from '@/config/eventBus' | |
| import TooltipLabel from '@/components/widgets/TooltipLabel' | |
| export default { | |
| name: 'LoadBalancing', | |
| mixins: [mixinForm], | |
| components: { | |
| Status, | |
| TooltipButton, | |
| BulkActionView, | |
| TooltipLabel | |
| }, | |
| props: { | |
| resource: { | |
| type: Object, | |
| required: true | |
| } | |
| }, | |
| inject: ['parentFetchData', 'parentToggleLoading'], | |
| data () { | |
| return { | |
| selectedRowKeys: [], | |
| showGroupActionModal: false, | |
| selectedItems: [], | |
| selectedColumns: [], | |
| filterColumns: ['State', 'Actions', 'Add VMs', 'Stickiness'], | |
| showConfirmationAction: false, | |
| message: { | |
| title: this.$t('label.action.bulk.delete.load.balancer.rules'), | |
| confirmMessage: this.$t('label.confirm.delete.loadbalancer.rules') | |
| }, | |
| loading: true, | |
| lbRules: [], | |
| tagsModalVisible: false, | |
| tagsModalLoading: false, | |
| tags: [], | |
| selectedRule: null, | |
| selectedTier: null, | |
| stickinessModalVisible: false, | |
| stickinessPolicies: [], | |
| stickinessModalLoading: false, | |
| selectedStickinessPolicy: null, | |
| stickinessPolicyMethod: 'LbCookie', | |
| editRuleModalVisible: false, | |
| editRuleModalLoading: false, | |
| editRuleDetails: { | |
| name: '', | |
| algorithm: '', | |
| protocol: '', | |
| cidrlist: '' | |
| }, | |
| newRule: { | |
| algorithm: 'roundrobin', | |
| name: '', | |
| privateport: '', | |
| publicport: '', | |
| protocol: 'tcp', | |
| virtualmachineid: [], | |
| vmguestip: [], | |
| cidrlist: '' | |
| }, | |
| lbProvider: null, | |
| addVmModalVisible: false, | |
| addVmModalLoading: false, | |
| addVmModalNicLoading: false, | |
| zoneloading: false, | |
| vms: [], | |
| nics: [], | |
| totalCount: 0, | |
| page: 1, | |
| pageSize: 10, | |
| sslcerts: { | |
| loading: false, | |
| data: [] | |
| }, | |
| selectedSsl: { | |
| name: '', | |
| id: null | |
| }, | |
| addSslCertModalVisible: false, | |
| showAssignedSsl: false, | |
| currentAccountId: null, | |
| assignedSslCert: 'None', | |
| deleteSslButtonVisible: true, | |
| addSslButtonVisible: true, | |
| columns: [ | |
| { | |
| title: this.$t('label.name'), | |
| dataIndex: 'name' | |
| }, | |
| { | |
| title: this.$t('label.publicport'), | |
| dataIndex: 'publicport' | |
| }, | |
| { | |
| title: this.$t('label.privateport'), | |
| dataIndex: 'privateport' | |
| }, | |
| { | |
| key: 'cidrlist', | |
| title: this.$t('label.sourcecidrlist') | |
| }, | |
| { | |
| key: 'protocol', | |
| title: this.$t('label.protocol') | |
| }, | |
| { | |
| title: this.$t('label.state'), | |
| dataIndex: 'state' | |
| }, | |
| { | |
| key: 'stickiness', | |
| title: this.$t('label.action.configure.stickiness') | |
| }, | |
| { | |
| key: 'add', | |
| title: this.$t('label.add.vms') | |
| }, | |
| { | |
| key: 'sslcert', | |
| title: this.$t('label.sslcertificate') | |
| }, | |
| { | |
| key: 'autoscale', | |
| title: this.$t('label.autoscale') | |
| }, | |
| { | |
| key: 'actions', | |
| title: this.$t('label.actions') | |
| } | |
| ], | |
| tiers: { | |
| loading: false, | |
| data: [] | |
| }, | |
| vmColumns: [ | |
| { | |
| key: 'name', | |
| title: this.$t('label.name'), | |
| dataIndex: 'name', | |
| width: 220 | |
| }, | |
| { | |
| key: 'state', | |
| title: this.$t('label.state'), | |
| dataIndex: 'state' | |
| }, | |
| { | |
| title: this.$t('label.displayname'), | |
| dataIndex: 'displayname' | |
| }, | |
| { | |
| title: this.$t('label.account'), | |
| dataIndex: 'account' | |
| }, | |
| { | |
| title: this.$t('label.zonename'), | |
| dataIndex: 'zonename' | |
| }, | |
| { | |
| key: 'actions', | |
| title: this.$t('label.select'), | |
| dataIndex: 'actions', | |
| width: 80 | |
| } | |
| ], | |
| vmPage: 1, | |
| vmPageSize: 10, | |
| vmCount: 0, | |
| addNetworkModalVisible: false, | |
| addNetworkModalLoading: false, | |
| networks: [], | |
| associatednetworkid: null, | |
| selectedTierForAutoScaling: null, | |
| networkColumns: [ | |
| { | |
| key: 'name', | |
| title: this.$t('label.name'), | |
| dataIndex: 'name', | |
| width: 220 | |
| }, | |
| { | |
| key: 'state', | |
| title: this.$t('label.state'), | |
| dataIndex: 'state' | |
| }, | |
| { | |
| title: this.$t('label.gateway'), | |
| dataIndex: 'gateway' | |
| }, | |
| { | |
| title: this.$t('label.netmask'), | |
| dataIndex: 'netmask' | |
| }, | |
| { | |
| key: 'actions', | |
| title: this.$t('label.select'), | |
| dataIndex: 'actions', | |
| width: 80 | |
| } | |
| ], | |
| networkPage: 1, | |
| networkPageSize: 10, | |
| networkCount: 0, | |
| searchQuery: null, | |
| tungstenHealthMonitors: [], | |
| healthMonitorModal: false, | |
| healthMonitorParams: { | |
| type: 'PING', | |
| retry: 3, | |
| timeout: 5, | |
| interval: 5, | |
| httpmethodtype: 'GET', | |
| expectedcode: undefined, | |
| urlpath: '/' | |
| }, | |
| healthMonitorLoading: false, | |
| isNetrisZone: false | |
| } | |
| }, | |
| computed: { | |
| hasSelected () { | |
| return this.selectedRowKeys.length > 0 | |
| } | |
| }, | |
| beforeCreate () { | |
| this.createLoadBalancerRuleParams = this.$getApiParams('createLoadBalancerRule') | |
| this.createLoadBalancerStickinessPolicyParams = this.$getApiParams('createLBStickinessPolicy') | |
| if ('associatednetworkid' in this.resource) { | |
| this.associatednetworkid = this.resource.associatednetworkid | |
| } | |
| }, | |
| created () { | |
| this.initForm() | |
| this.initMonitorForm() | |
| this.fetchData() | |
| }, | |
| watch: { | |
| resource: { | |
| deep: true, | |
| handler (newItem) { | |
| if (!newItem || !newItem.id) { | |
| return | |
| } | |
| this.fetchData() | |
| } | |
| } | |
| }, | |
| methods: { | |
| initForm () { | |
| this.formRef = ref() | |
| this.form = reactive({}) | |
| this.rules = reactive({}) | |
| }, | |
| initMonitorForm () { | |
| this.monitorRef = ref() | |
| this.monitorForm = reactive({ | |
| type: this.healthMonitorParams.type, | |
| retry: this.healthMonitorParams.retry, | |
| timeout: this.healthMonitorParams.timeout, | |
| interval: this.healthMonitorParams.interval, | |
| httpmethodtype: this.healthMonitorParams.httpmethodtype, | |
| expectedcode: this.healthMonitorParams.expectedcode, | |
| urlpath: this.healthMonitorParams.urlpath | |
| }) | |
| this.monitorRules = reactive({ | |
| retry: [{ required: true, message: this.$t('message.error.required.input') }], | |
| timeout: [{ required: true, message: this.$t('message.error.required.input') }], | |
| interval: [{ required: true, message: this.$t('message.error.required.input') }], | |
| expectedcode: [{ required: true, message: this.$t('message.error.required.input') }], | |
| urlpath: [{ required: true, message: this.$t('message.error.required.input') }] | |
| }) | |
| }, | |
| fetchData () { | |
| this.fetchListTiers() | |
| this.fetchLBRules() | |
| this.fetchZone() | |
| }, | |
| fetchListTiers () { | |
| this.tiers.loading = true | |
| getAPI('listNetworks', { | |
| supportedservices: 'Lb', | |
| isrecursive: true, | |
| vpcid: this.resource.vpcid | |
| }).then(json => { | |
| this.tiers.data = json.listnetworksresponse.network || [] | |
| this.selectedTier = this.tiers.data?.[0]?.id ? this.tiers.data[0].id : null | |
| if (this.tiers.data?.[0]?.broadcasturi === 'tf://tf') { | |
| this.columns.splice(8, 0, { | |
| title: this.$t('label.action.health.monitor'), | |
| key: 'healthmonitor' | |
| }) | |
| } | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { this.tiers.loading = false }) | |
| }, | |
| fetchLBRules () { | |
| this.loading = true | |
| this.lbRules = [] | |
| this.stickinessPolicies = [] | |
| getAPI('listLoadBalancerRules', { | |
| listAll: true, | |
| publicipid: this.resource.id, | |
| page: this.page, | |
| pageSize: this.pageSize | |
| }).then(response => { | |
| this.lbRules = response.listloadbalancerrulesresponse.loadbalancerrule || [] | |
| this.totalCount = response.listloadbalancerrulesresponse.count || 0 | |
| }).then(() => { | |
| if (this.lbRules.length > 0) { | |
| setTimeout(() => { | |
| this.fetchLBRuleInstances() | |
| }, 100) | |
| this.fetchLBStickinessPolicies() | |
| this.fetchLBTungstenFabricHealthMonitor() | |
| this.fetchAutoScaleVMgroups() | |
| return | |
| } | |
| this.loading = false | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.loading = false | |
| }) | |
| }, | |
| fetchLBRuleInstances () { | |
| for (const rule of this.lbRules) { | |
| this.loading = true | |
| getAPI('listLoadBalancerRuleInstances', { | |
| listAll: true, | |
| lbvmips: true, | |
| id: rule.id | |
| }).then(response => { | |
| rule.ruleInstances = response.listloadbalancerruleinstancesresponse.lbrulevmidip | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| } | |
| }, | |
| fetchLBStickinessPolicies () { | |
| this.loading = true | |
| this.lbRules.forEach(rule => { | |
| getAPI('listLBStickinessPolicies', { | |
| listAll: true, | |
| lbruleid: rule.id | |
| }).then(response => { | |
| this.stickinessPolicies.push(...response.listlbstickinesspoliciesresponse.stickinesspolicies) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| }) | |
| }, | |
| fetchAutoScaleVMgroups () { | |
| this.loading = true | |
| this.lbRules.forEach(rule => { | |
| getAPI('listAutoScaleVmGroups', { | |
| listAll: true, | |
| lbruleid: rule.id | |
| }).then(response => { | |
| rule.autoscalevmgroup = response.listautoscalevmgroupsresponse?.autoscalevmgroup?.[0] | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| }) | |
| }, | |
| fetchZone () { | |
| this.zoneloading = true | |
| getAPI('listZones', { | |
| id: this.resource.zoneid | |
| }).then(response => { | |
| this.lbProvider = response?.listzonesresponse?.zone?.[0]?.provider || null | |
| if (this.lbProvider != null) { | |
| this.isNetrisZone = this.lbProvider === 'Netris' | |
| } | |
| }).finally(() => { | |
| this.zoneloading = false | |
| if (this.lbProvider !== 'Netris') { | |
| this.column.push({ | |
| key: 'algorithm', | |
| title: this.$t('label.algorithm') | |
| }) | |
| } | |
| }) | |
| }, | |
| fetchSslCerts () { | |
| this.sslcerts.loading = true | |
| this.sslcerts.data = [] | |
| // First get the account id | |
| getAPI('listAccounts', { | |
| name: this.resource.account, | |
| domainid: this.resource.domainid | |
| }).then(json => { | |
| const accounts = json.listaccountsresponse.account || [] | |
| if (accounts.length > 0) { | |
| // Now fetch all the ssl certs for this account | |
| this.currentAccountId = accounts[0].id | |
| getAPI('listSslCerts', { | |
| accountid: this.currentAccountId | |
| }).then(json => { | |
| json.listsslcertsresponse.sslcert.forEach(entry => this.sslcerts.data.push(entry)) | |
| if (json.listsslcertsresponse.sslcert && json.listsslcertsresponse.sslcert.length > 0 && this.selectedSsl.id == null) { | |
| this.selectedSsl.name = json.listsslcertsresponse.sslcert[0].name | |
| this.selectedSsl.id = json.listsslcertsresponse.sslcert[0].id | |
| } | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }) | |
| } | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.sslcerts.loading = false | |
| }) | |
| if (this.selectedRule !== null) { | |
| this.getCurrentAssignedSslCert() | |
| } | |
| }, | |
| getCurrentAssignedSslCert () { | |
| getAPI('listSslCerts', { | |
| accountid: this.currentAccountId, | |
| lbruleid: this.selectedRule.id | |
| }).then(json => { | |
| if (json.listsslcertsresponse.sslcert && json.listsslcertsresponse.sslcert.length > 0) { | |
| this.assignedSslCert = json.listsslcertsresponse.sslcert[0].name | |
| this.deleteSslButtonVisible = true | |
| } else { | |
| this.assignedSslCert = 'None' | |
| this.deleteSslButtonVisible = false | |
| } | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }) | |
| }, | |
| selectssl (e) { | |
| this.selectedSsl.id = e | |
| const sslcert = this.sslcerts.data.find(entry => entry.id === this.selectedSsl.id) | |
| if (sslcert) { | |
| this.selectedSsl.name = sslcert.name | |
| } | |
| }, | |
| handleAddSslCert (data) { | |
| this.addSslCert(data, this.selectedSsl.id) | |
| }, | |
| addSslTolbRule () { | |
| this.visible = false | |
| this.addSslCert(this.selectedRule.id, this.selectedSsl.id) | |
| }, | |
| addSslCert (lbRuleId, certId) { | |
| this.disableSslAddDeleteButtons() | |
| getAPI('assignCertToLoadBalancer', { | |
| lbruleid: lbRuleId, | |
| certid: certId, | |
| forced: true | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.assigncerttoloadbalancerresponse.jobid, | |
| successMessage: this.$t('message.success.assign.sslcert'), | |
| successMethod: () => { | |
| if (this.selectedRule !== null) { | |
| this.getCurrentAssignedSslCert() | |
| } | |
| this.enableSslAddDeleteButtons() | |
| }, | |
| errorMessage: this.$t('message.assign.sslcert.failed'), | |
| errorMethod: () => { | |
| }, | |
| loadingMessage: this.$t('message.assign.sslcert.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: (e) => { | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| }) | |
| }, | |
| removeSslFromLbRule () { | |
| this.disableSslAddDeleteButtons() | |
| getAPI('removeCertFromLoadBalancer', { | |
| lbruleid: this.selectedRule.id | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.removecertfromloadbalancerresponse.jobid, | |
| successMessage: this.$t('message.success.remove.sslcert'), | |
| successMethod: () => { | |
| this.visible = true | |
| this.getCurrentAssignedSslCert() | |
| this.enableSslAddDeleteButtons() | |
| }, | |
| errorMessage: this.$t('message.remove.sslcert.failed'), | |
| errorMethod: () => { | |
| this.visible = true | |
| }, | |
| loadingMessage: this.$t('message.remove.sslcert.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| }) | |
| }, | |
| enableSslAddDeleteButtons () { | |
| this.deleteSslButtonVisible = true | |
| this.addSslButtonVisible = true | |
| }, | |
| disableSslAddDeleteButtons () { | |
| this.addSslButtonVisible = false | |
| this.deleteSslButtonVisible = false | |
| }, | |
| handleOpenAddSslCertModal (record) { | |
| this.addSslCertModalVisible = true | |
| if (record) { | |
| this.showAssignedSsl = true | |
| this.addSslButtonVisible = true | |
| this.selectedSsl = {} | |
| } else { | |
| this.showAssignedSsl = false | |
| this.addSslButtonVisible = false | |
| } | |
| this.fetchSslCerts() | |
| }, | |
| returnAlgorithmName (name) { | |
| switch (name) { | |
| case 'leastconn': | |
| return 'Least connections' | |
| case 'roundrobin' : | |
| return 'Round-robin' | |
| case 'source': | |
| return 'Source' | |
| default : | |
| return '' | |
| } | |
| }, | |
| returnStickinessLabel (id) { | |
| const match = this.stickinessPolicies.filter(policy => policy.lbruleid === id) | |
| if (match.length > 0 && match[0].stickinesspolicy.length > 0) { | |
| return match[0].stickinesspolicy[0].methodname | |
| } | |
| return 'Configure' | |
| }, | |
| getCapitalise (val) { | |
| if (!val) { | |
| return | |
| } | |
| if (val === 'all') return this.$t('label.all') | |
| return val.toUpperCase() | |
| }, | |
| openTagsModal (id) { | |
| this.initForm() | |
| this.rules = { | |
| key: [{ required: true, message: this.$t('message.specify.tag.key') }], | |
| value: [{ required: true, message: this.$t('message.specify.tag.value') }] | |
| } | |
| this.tagsModalLoading = true | |
| this.tagsModalVisible = true | |
| this.tags = [] | |
| this.selectedRule = id | |
| getAPI('listTags', { | |
| resourceId: id, | |
| resourceType: 'LoadBalancer', | |
| listAll: true | |
| }).then(response => { | |
| this.tags = response.listtagsresponse.tag | |
| this.tagsModalLoading = false | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.closeModal() | |
| }) | |
| }, | |
| handleAddTag (e) { | |
| if (this.tagsModalLoading) return | |
| this.tagsModalLoading = true | |
| e.preventDefault() | |
| this.formRef.value.validate().then(() => { | |
| const formRaw = toRaw(this.form) | |
| const values = this.handleRemoveFields(formRaw) | |
| postAPI('createTags', { | |
| 'tags[0].key': values.key, | |
| 'tags[0].value': values.value, | |
| resourceIds: this.selectedRule, | |
| resourceType: 'LoadBalancer' | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.createtagsresponse.jobid, | |
| successMessage: this.$t('message.success.add.tag'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.openTagsModal(this.selectedRule) | |
| }, | |
| errorMessage: this.$t('message.add.tag.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.add.tag.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }) | |
| }).catch(error => { | |
| this.formRef.value.scrollToField(error.errorFields[0].name) | |
| }).finally(() => { | |
| this.tagsModalLoading = false | |
| }) | |
| }, | |
| handleDeleteTag (tag) { | |
| this.tagsModalLoading = true | |
| postAPI('deleteTags', { | |
| 'tags[0].key': tag.key, | |
| 'tags[0].value': tag.value, | |
| resourceIds: tag.resourceid, | |
| resourceType: 'LoadBalancer' | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.deletetagsresponse.jobid, | |
| successMessage: this.$t('message.success.delete.tag'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.openTagsModal(this.selectedRule) | |
| }, | |
| errorMessage: this.$t('message.delete.tag.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.delete.tag.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }) | |
| }, | |
| openStickinessModal (id) { | |
| this.initForm() | |
| this.rules = { | |
| methodname: [{ required: true, message: this.$t('message.error.specify.stickiness.method') }] | |
| } | |
| this.stickinessModalVisible = true | |
| this.selectedRule = id | |
| const match = this.stickinessPolicies.find(policy => policy.lbruleid === id) | |
| if (match && match.stickinesspolicy.length > 0) { | |
| this.selectedStickinessPolicy = match.stickinesspolicy[0] | |
| this.stickinessPolicyMethod = this.selectedStickinessPolicy.methodname | |
| nextTick().then(() => { | |
| this.form.methodname = this.selectedStickinessPolicy.methodname | |
| this.form.name = this.selectedStickinessPolicy.name | |
| this.form.cookieName = this.selectedStickinessPolicy.params['cookie-name'] | |
| this.form.mode = this.selectedStickinessPolicy.params.mode | |
| this.form.domain = this.selectedStickinessPolicy.params.domain | |
| this.form.length = this.selectedStickinessPolicy.params.length | |
| this.form.holdtime = this.selectedStickinessPolicy.params.holdtime | |
| this.form.nocache = !!this.selectedStickinessPolicy.params.nocache | |
| this.form.indirect = !!this.selectedStickinessPolicy.params.indirect | |
| this.form.postonly = !!this.selectedStickinessPolicy.params.postonly | |
| this.form.requestLearn = !!this.selectedStickinessPolicy.params['request-learn'] | |
| this.form.prefix = !!this.selectedStickinessPolicy.params.prefix | |
| }) | |
| } | |
| }, | |
| handleAddStickinessPolicy (data, values) { | |
| postAPI('createLBStickinessPolicy', { | |
| ...data, | |
| lbruleid: this.selectedRule, | |
| name: values.name, | |
| methodname: values.methodname | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.createLBStickinessPolicy.jobid, | |
| successMessage: this.$t('message.success.config.sticky.policy'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| errorMessage: this.$t('message.config.sticky.policy.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.config.sticky.policy.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.closeModal() | |
| }) | |
| }, | |
| handleDeleteStickinessPolicy () { | |
| this.stickinessModalLoading = true | |
| postAPI('deleteLBStickinessPolicy', { id: this.selectedStickinessPolicy.id }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.deleteLBstickinessrruleresponse.jobid, | |
| successMessage: this.$t('message.success.remove.sticky.policy'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| errorMessage: this.$t('message.remove.sticky.policy.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.remove.sticky.policy.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }) | |
| }, | |
| handleSubmitStickinessForm (e) { | |
| if (this.stickinessModalLoading) return | |
| this.stickinessModalLoading = true | |
| e.preventDefault() | |
| this.formRef.value.validate().then(() => { | |
| const formRaw = toRaw(this.form) | |
| const values = this.handleRemoveFields(formRaw) | |
| if (values.methodname === 'none') { | |
| this.handleDeleteStickinessPolicy() | |
| return | |
| } | |
| if (values.name === null || values.name === undefined || values.name === '') { | |
| this.$notification.error({ | |
| message: this.$t('label.error'), | |
| description: this.$t('message.error.specify.sticky.name') | |
| }) | |
| return | |
| } | |
| values.nocache = this.form.nocache | |
| values.indirect = this.form.indirect | |
| values.postonly = this.form.postonly | |
| values.requestLearn = this.form.requestLearn | |
| values.prefix = this.form.prefix | |
| let data = {} | |
| let count = 0 | |
| Object.entries(values).forEach(([key, val]) => { | |
| if (val && key !== 'name' && key !== 'methodname') { | |
| if (key === 'cookieName') { | |
| data = { ...data, ...{ [`param[${count}].name`]: 'cookie-name' } } | |
| } else if (key === 'requestLearn') { | |
| data = { ...data, ...{ [`param[${count}].name`]: 'request-learn' } } | |
| } else { | |
| data = { ...data, ...{ [`param[${count}].name`]: key } } | |
| } | |
| data = { ...data, ...{ [`param[${count}].value`]: val } } | |
| count++ | |
| } | |
| }) | |
| this.handleAddStickinessPolicy(data, values) | |
| }).catch(error => { | |
| this.formRef.value.scrollToField(error.errorFields[0].name) | |
| }).finally(() => { | |
| this.stickinessModalLoading = false | |
| }) | |
| }, | |
| handleStickinessMethodSelectChange (e) { | |
| if (this.formRef.value) this.formRef.value.resetFields() | |
| this.stickinessPolicyMethod = e | |
| this.form.methodname = e | |
| }, | |
| handleDeleteInstanceFromRule (instance, rule, ip) { | |
| this.loading = true | |
| postAPI('removeFromLoadBalancerRule', { | |
| id: rule.id, | |
| 'vmidipmap[0].vmid': instance.loadbalancerruleinstance.id, | |
| 'vmidipmap[0].vmip': ip | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.removefromloadbalancerruleresponse.jobid, | |
| successMessage: this.$t('message.success.remove.instance.rule'), | |
| successMethod: () => { | |
| this.fetchData() | |
| }, | |
| errorMessage: this.$t('message.remove.instance.failed'), | |
| errorMethod: () => { | |
| this.fetchData() | |
| }, | |
| loadingMessage: this.$t('message.remove.instance.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.fetchData() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.fetchData() | |
| }) | |
| }, | |
| openEditRuleModal (rule) { | |
| this.selectedRule = rule | |
| this.editRuleModalVisible = true | |
| this.editRuleDetails.name = this.selectedRule.name | |
| this.editRuleDetails.algorithm = this.lbProvider !== 'Netris' ? this.selectedRule.algorithm : undefined | |
| this.editRuleDetails.protocol = this.selectedRule.protocol | |
| // Normalize cidrlist: replace spaces with commas and clean up | |
| this.editRuleDetails.cidrlist = (this.selectedRule.cidrlist || '') | |
| .split(/[\s,]+/) // Split on spaces or commas | |
| .map(c => c.trim()) | |
| .filter(c => c) | |
| .join(',') || '' | |
| }, | |
| handleSubmitEditForm () { | |
| if (this.editRuleModalLoading) return | |
| this.loading = true | |
| this.editRuleModalLoading = true | |
| const payload = { | |
| ...this.editRuleDetails, | |
| id: this.selectedRule.id, | |
| ...(this.editRuleDetails.cidrlist && { | |
| cidrList: (this.editRuleDetails.cidrlist || '').split(',').map(c => c.trim()).filter(c => c) | |
| }) | |
| } | |
| postAPI('updateLoadBalancerRule', { | |
| ...this.editRuleDetails, | |
| id: this.selectedRule.id, | |
| ...payload | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.updateloadbalancerruleresponse.jobid, | |
| successMessage: this.$t('message.success.edit.rule'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| errorMessage: this.$t('message.edit.rule.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.edit.rule.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.loading = false | |
| }) | |
| }, | |
| setSelection (selection) { | |
| this.selectedRowKeys = selection | |
| this.$emit('selection-change', this.selectedRowKeys) | |
| this.selectedItems = (this.lbRules.filter(function (item) { | |
| return selection.indexOf(item.id) !== -1 | |
| })) | |
| }, | |
| resetSelection () { | |
| this.setSelection([]) | |
| }, | |
| onSelectChange (selectedRowKeys, selectedRows) { | |
| this.setSelection(selectedRowKeys) | |
| }, | |
| bulkActionConfirmation () { | |
| this.showConfirmationAction = true | |
| this.selectedColumns = this.columns.filter(column => { | |
| return !this.filterColumns.includes(column.title) | |
| }) | |
| this.selectedItems = this.selectedItems.map(v => ({ ...v, status: 'InProgress' })) | |
| }, | |
| handleCancel () { | |
| eventBus.emit('update-bulk-job-status', { items: this.selectedItems, action: false }) | |
| this.showGroupActionModal = false | |
| this.selectedItems = [] | |
| this.selectedColumns = [] | |
| this.selectedRowKeys = [] | |
| this.parentFetchData() | |
| }, | |
| deleteRules (e) { | |
| this.showConfirmationAction = false | |
| this.selectedColumns.splice(0, 0, { | |
| key: 'status', | |
| dataIndex: 'status', | |
| title: this.$t('label.operation.status'), | |
| filters: [ | |
| { text: 'In Progress', value: 'InProgress' }, | |
| { text: 'Success', value: 'success' }, | |
| { text: 'Failed', value: 'failed' } | |
| ] | |
| }) | |
| if (this.selectedRowKeys.length > 0) { | |
| this.showGroupActionModal = true | |
| } | |
| for (const rule of this.selectedItems) { | |
| this.handleDeleteRule(rule) | |
| } | |
| }, | |
| handleDeleteRule (rule) { | |
| this.loading = true | |
| postAPI('deleteLoadBalancerRule', { | |
| id: rule.id | |
| }).then(response => { | |
| const jobId = response.deleteloadbalancerruleresponse.jobid | |
| eventBus.emit('update-job-details', { jobId, resourceId: null }) | |
| this.$pollJob({ | |
| title: this.$t('label.action.delete.load.balancer'), | |
| description: rule.id, | |
| jobId: jobId, | |
| successMessage: this.$t('message.success.remove.rule'), | |
| successMethod: () => { | |
| if (this.selectedItems.length > 0) { | |
| eventBus.emit('update-resource-state', { selectedItems: this.selectedItems, resource: rule.id, state: 'success' }) | |
| } | |
| if (this.selectedRowKeys.length === 0) { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| } | |
| this.closeModal() | |
| }, | |
| errorMessage: this.$t('message.remove.rule.failed'), | |
| errorMethod: () => { | |
| if (this.selectedItems.length > 0) { | |
| eventBus.emit('update-resource-state', { selectedItems: this.selectedItems, resource: rule.id, state: 'failed' }) | |
| } | |
| if (this.selectedRowKeys.length === 0) { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| } | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.delete.rule.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| if (this.selectedRowKeys.length === 0) { | |
| this.parentToggleLoading() | |
| this.parentFetchData() | |
| } | |
| this.closeModal() | |
| }, | |
| bulkAction: `${this.selectedItems.length > 0}` && this.showGroupActionModal | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.loading = false | |
| }) | |
| }, | |
| checkNewRule () { | |
| if (!this.selectedRule) { | |
| if (!this.newRule.name) { | |
| this.$refs.newRuleName.classList.add('error') | |
| } else { | |
| this.$refs.newRuleName.classList.remove('error') | |
| } | |
| if (!this.newRule.publicport) { | |
| this.$refs.newRulePublicPort.classList.add('error') | |
| } else { | |
| this.$refs.newRulePublicPort.classList.remove('error') | |
| } | |
| if (!this.newRule.privateport) { | |
| this.$refs.newRulePrivatePort.classList.add('error') | |
| } else { | |
| this.$refs.newRulePrivatePort.classList.remove('error') | |
| } | |
| if (!this.newRule.name || !this.newRule.publicport || !this.newRule.privateport) return false | |
| } | |
| return true | |
| }, | |
| handleOpenAddVMModal () { | |
| if (this.addVmModalLoading) return | |
| if (!this.checkNewRule()) { | |
| return | |
| } | |
| this.addVmModalVisible = true | |
| this.fetchVirtualMachines() | |
| }, | |
| fetchNics (e, index) { | |
| if (!e.target.checked) { | |
| this.newRule.virtualmachineid[index] = null | |
| this.nics[index] = null | |
| this.newRule.vmguestip[index] = null | |
| return | |
| } | |
| this.newRule.virtualmachineid[index] = e.target.value | |
| this.addVmModalNicLoading = true | |
| getAPI('listNics', { | |
| virtualmachineid: e.target.value, | |
| networkid: ('vpcid' in this.resource && !('associatednetworkid' in this.resource)) ? this.selectedTier : this.resource.associatednetworkid | |
| }).then(response => { | |
| if (!response || !response.listnicsresponse || !response.listnicsresponse.nic[0]) return | |
| const newItem = [] | |
| newItem.push(response.listnicsresponse.nic[0].ipaddress) | |
| if (response.listnicsresponse.nic[0].secondaryip) { | |
| newItem.push(...response.listnicsresponse.nic[0].secondaryip.map(ip => ip.ipaddress)) | |
| } | |
| this.nics[index] = newItem | |
| this.newRule.vmguestip[index] = [this.nics[index][0]] | |
| this.addVmModalNicLoading = false | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.closeModal() | |
| }) | |
| }, | |
| fetchVirtualMachines () { | |
| this.vmCount = 0 | |
| this.vms = [] | |
| this.addVmModalLoading = true | |
| const networkId = ('vpcid' in this.resource && !('associatednetworkid' in this.resource)) ? this.selectedTier : this.resource.associatednetworkid | |
| if (!networkId) { | |
| this.addVmModalLoading = false | |
| return | |
| } | |
| getAPI('listVirtualMachines', { | |
| listAll: true, | |
| keyword: this.searchQuery, | |
| page: this.vmPage, | |
| pagesize: this.vmPageSize, | |
| networkid: networkId | |
| }).then(response => { | |
| this.vmCount = response.listvirtualmachinesresponse.count || 0 | |
| this.vms = response.listvirtualmachinesresponse.virtualmachine || [] | |
| this.vms.forEach((vm, index) => { | |
| this.newRule.virtualmachineid[index] = null | |
| this.nics[index] = null | |
| this.newRule.vmguestip[index] = null | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.addVmModalLoading = false | |
| }) | |
| }, | |
| handleOpenAddNetworkModal () { | |
| if (this.addNetworkModalLoading) return | |
| if (!this.checkNewRule()) { | |
| return | |
| } | |
| this.addNetworkModalVisible = true | |
| this.fetchNetworks() | |
| }, | |
| fetchNetworks () { | |
| this.networkCount = 0 | |
| this.networks = [] | |
| this.addNetworkModalLoading = true | |
| const vpcid = this.resource.vpcid | |
| if (!vpcid) { | |
| this.addNetworkModalLoading = false | |
| return | |
| } | |
| getAPI('listNetworks', { | |
| listAll: true, | |
| keyword: this.searchQuery, | |
| page: this.networkPage, | |
| pagesize: this.networkPageSize, | |
| supportedservices: 'Lb', | |
| isrecursive: true, | |
| vpcid: vpcid | |
| }).then(response => { | |
| this.networkCount = response.listnetworksresponse.count || 0 | |
| this.networks = response.listnetworksresponse.network || [] | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.addNetworkModalLoading = false | |
| }) | |
| this.selectedTierForAutoScaling = null | |
| }, | |
| onNetworkSearch (value) { | |
| this.searchQuery = value | |
| this.fetchNetworks() | |
| }, | |
| handleChangeNetworkPage (page, pageSize) { | |
| this.networkPage = page | |
| this.networkPageSize = pageSize | |
| this.fetchNetworks() | |
| }, | |
| handleChangeNetworkPageSize (currentPage, pageSize) { | |
| this.networkPage = currentPage | |
| this.networkPageSize = pageSize | |
| this.fetchNetworks() | |
| }, | |
| handleAssignToLBRule (data) { | |
| const vmIDIpMap = {} | |
| let selectedVmCount = 0 | |
| let count = 0 | |
| let innerCount = 0 | |
| this.newRule.vmguestip.forEach(ip => { | |
| if (Array.isArray(ip)) { | |
| ip.forEach(i => { | |
| vmIDIpMap[`vmidipmap[${innerCount}].vmid`] = this.newRule.virtualmachineid[count] | |
| vmIDIpMap[`vmidipmap[${innerCount}].vmip`] = i | |
| innerCount++ | |
| }) | |
| } else { | |
| vmIDIpMap[`vmidipmap[${innerCount}].vmid`] = this.newRule.virtualmachineid[count] | |
| vmIDIpMap[`vmidipmap[${innerCount}].vmip`] = ip | |
| innerCount++ | |
| } | |
| if (this.newRule.virtualmachineid[count]) { | |
| selectedVmCount++ | |
| } | |
| count++ | |
| }) | |
| if (selectedVmCount === 0) { | |
| this.fetchData() | |
| return | |
| } | |
| this.loading = true | |
| postAPI('assignToLoadBalancerRule', { | |
| id: data, | |
| ...vmIDIpMap | |
| }).then(response => { | |
| this.$pollJob({ | |
| jobId: response.assigntoloadbalancerruleresponse.jobid, | |
| successMessage: this.$t('message.success.assign.vm'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| if (this.newRule.protocol === 'ssl' && this.selectedSsl.id !== null) { | |
| this.handleAddSslCert(data) | |
| } | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| errorMessage: this.$t('message.assign.vm.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| }, | |
| loadingMessage: this.$t('message.assign.vm.processing'), | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentFetchData() | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeModal() | |
| } | |
| }) | |
| }) | |
| }, | |
| handleAddNewRule () { | |
| if (this.loading) return | |
| this.loading = true | |
| if (this.selectedRule) { | |
| this.handleAssignToLBRule(this.selectedRule.id) | |
| return | |
| } else if (!this.checkNewRule()) { | |
| this.loading = false | |
| return | |
| } | |
| const networkId = this.selectedTierForAutoScaling != null ? this.selectedTierForAutoScaling | |
| : ('vpcid' in this.resource && !('associatednetworkid' in this.resource)) ? this.selectedTier : this.resource.associatednetworkid | |
| postAPI('createLoadBalancerRule', { | |
| openfirewall: false, | |
| networkid: networkId, | |
| publicipid: this.resource.id, | |
| algorithm: this.newRule.algorithm, | |
| name: this.newRule.name, | |
| privateport: this.newRule.privateport, | |
| protocol: this.newRule.protocol, | |
| publicport: this.newRule.publicport, | |
| cidrlist: this.newRule.cidrlist | |
| }).then(response => { | |
| this.addVmModalVisible = false | |
| this.addNetworkModalVisible = false | |
| this.handleAssignToLBRule(response.createloadbalancerruleresponse.id) | |
| this.associatednetworkid = networkId | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| this.loading = false | |
| }) | |
| // assigntoloadbalancerruleresponse.jobid | |
| }, | |
| closeModal () { | |
| this.selectedRule = null | |
| this.tagsModalVisible = false | |
| this.stickinessModalVisible = false | |
| this.stickinessModalLoading = false | |
| this.selectedStickinessPolicy = null | |
| this.stickinessPolicyMethod = 'LbCookie' | |
| this.editRuleModalVisible = false | |
| this.editRuleModalLoading = false | |
| this.addVmModalLoading = false | |
| this.addVmModalNicLoading = false | |
| this.showConfirmationAction = false | |
| this.vms = [] | |
| this.nics = [] | |
| this.addVmModalVisible = false | |
| this.newRule.virtualmachineid = [] | |
| this.addNetworkModalLoading = false | |
| this.addNetworkModalVisible = false | |
| this.selectedTierForAutoScaling = null | |
| this.addSslCertModalVisible = null | |
| }, | |
| handleChangePage (page, pageSize) { | |
| this.page = page | |
| this.pageSize = pageSize | |
| this.fetchData() | |
| }, | |
| handleChangePageSize (currentPage, pageSize) { | |
| this.page = currentPage | |
| this.pageSize = pageSize | |
| this.fetchData() | |
| }, | |
| handleChangeVmPage (page, pageSize) { | |
| this.vmPage = page | |
| this.vmPageSize = pageSize | |
| this.fetchVirtualMachines() | |
| }, | |
| handleChangeVmPageSize (currentPage, pageSize) { | |
| this.vmPage = currentPage | |
| this.vmPageSize = pageSize | |
| this.fetchVirtualMachines() | |
| }, | |
| onSearch (value) { | |
| this.searchQuery = value | |
| this.fetchVirtualMachines() | |
| }, | |
| fetchLBTungstenFabricHealthMonitor () { | |
| if (!('listTungstenFabricLBHealthMonitor' in this.$store.getters.apis)) { | |
| return | |
| } | |
| this.tungstenHealthMonitors = [] | |
| this.loading = true | |
| this.lbRules.forEach(rule => { | |
| getAPI('listTungstenFabricLBHealthMonitor', { | |
| listAll: true, | |
| lbruleid: rule.id | |
| }).then(response => { | |
| const healthmonitor = response?.listtungstenfabriclbhealthmonitorresponse?.healthmonitor || [] | |
| if (healthmonitor.length > 0) { | |
| healthmonitor[0].lbruleid = rule.id | |
| this.tungstenHealthMonitors.push(...healthmonitor) | |
| } | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.loading = false | |
| }) | |
| }) | |
| }, | |
| returnHealthMonitorLabel (id) { | |
| const match = this.tungstenHealthMonitors.filter(item => item.lbruleid === id) | |
| if (match.length > 0) { | |
| return match[0].type | |
| } | |
| return this.$t('label.configure') | |
| }, | |
| openHealthMonitorModal (id) { | |
| const match = this.tungstenHealthMonitors.filter(item => item.lbruleid === id) | |
| this.healthMonitorParams.lbruleid = id | |
| if (match.length > 0) { | |
| this.healthMonitorParams.type = match[0].type | |
| this.healthMonitorParams.retry = match[0].retry | |
| this.healthMonitorParams.timeout = match[0].timeout | |
| this.healthMonitorParams.interval = match[0].interval | |
| this.healthMonitorParams.httpmethodtype = match[0].httpmethod | |
| this.healthMonitorParams.expectedcode = match[0].expectedcode | |
| this.healthMonitorParams.urlpath = match[0].urlpath | |
| } | |
| this.initMonitorForm() | |
| this.healthMonitorModal = true | |
| }, | |
| closeMonitorModal () { | |
| this.healthMonitorModal = false | |
| this.healthMonitorParams = { | |
| type: 'PING', | |
| retry: 3, | |
| timeout: 5, | |
| interval: 5, | |
| httpmethodtype: 'GET', | |
| expectedcode: undefined, | |
| urlpath: '/' | |
| } | |
| }, | |
| handleConfigHealthMonitor () { | |
| if (this.healthMonitorLoading) return | |
| this.monitorRef.value.validate().then(() => { | |
| const values = toRaw(this.monitorForm) | |
| this.healthMonitorParams.type = values.type | |
| this.healthMonitorParams.retry = values.retry | |
| this.healthMonitorParams.timeout = values.timeout | |
| this.healthMonitorParams.interval = values.interval | |
| if (values.type === 'HTTP') { | |
| this.healthMonitorParams.httpmethodtype = values.httpmethodtype | |
| this.healthMonitorParams.expectedcode = values.expectedcode | |
| this.healthMonitorParams.urlpath = values.urlpath | |
| } | |
| this.healthMonitorLoading = true | |
| postAPI('updateTungstenFabricLBHealthMonitor', this.healthMonitorParams).then(json => { | |
| const jobId = json?.updatetungstenfabriclbhealthmonitorresponse?.jobid | |
| this.$pollJob({ | |
| jobId: jobId, | |
| successMessage: this.$t('message.success.config.health.monitor'), | |
| successMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeMonitorModal() | |
| this.healthMonitorLoading = false | |
| }, | |
| errorMessage: this.$t('message.config.health.monitor.failed'), | |
| errorMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeMonitorModal() | |
| this.healthMonitorLoading = false | |
| }, | |
| catchMessage: this.$t('error.fetching.async.job.result'), | |
| catchMethod: () => { | |
| this.parentToggleLoading() | |
| this.fetchData() | |
| this.closeMonitorModal() | |
| this.healthMonitorLoading = false | |
| } | |
| }) | |
| }).catch(error => { | |
| this.$notifyError(error) | |
| }).finally(() => { | |
| this.healthMonitorLoading = false | |
| }) | |
| }).catch((error) => { | |
| this.monitorRef.value.scrollToField(error.errorFields[0].name) | |
| }).finally(() => { | |
| this.healthMonitorLoading = false | |
| }) | |
| } | |
| } | |
| } | |
| </script> | |
| <style lang="scss" scoped> | |
| .rule { | |
| &-container { | |
| display: flex; | |
| flex-direction: column; | |
| width: 100%; | |
| @media (min-width: 760px) { | |
| margin-right: -20px; | |
| margin-bottom: -10px; | |
| } | |
| } | |
| &__row { | |
| display: flex; | |
| flex-wrap: wrap; | |
| } | |
| &__item { | |
| padding-right: 20px; | |
| margin-bottom: 20px; | |
| @media (min-width: 760px) { | |
| flex: 1; | |
| } | |
| } | |
| &__title { | |
| font-weight: bold; | |
| } | |
| } | |
| .add-btn { | |
| width: 100%; | |
| padding-top: 15px; | |
| padding-bottom: 15px; | |
| height: auto; | |
| } | |
| .add-actions { | |
| display: flex; | |
| justify-content: flex-end; | |
| margin-right: -20px; | |
| margin-bottom: 20px; | |
| @media (min-width: 760px) { | |
| margin-top: 20px; | |
| } | |
| button { | |
| margin-right: 20px; | |
| } | |
| } | |
| .form { | |
| display: flex; | |
| margin-right: -20px; | |
| flex-direction: column; | |
| align-items: flex-start; | |
| @media (min-width: 760px) { | |
| flex-direction: row; | |
| } | |
| &__required { | |
| margin-right: 5px; | |
| color: red; | |
| } | |
| .error-text { | |
| display: none; | |
| color: red; | |
| font-size: 0.8rem; | |
| } | |
| .error { | |
| input { | |
| border-color: red; | |
| } | |
| .error-text { | |
| display: block; | |
| } | |
| } | |
| &--column { | |
| flex-direction: column; | |
| margin-right: 0; | |
| align-items: flex-end; | |
| .form__item { | |
| width: 100%; | |
| padding-right: 0; | |
| } | |
| } | |
| &__item { | |
| display: flex; | |
| flex-direction: column; | |
| padding-right: 20px; | |
| margin-bottom: 20px; | |
| @media (min-width: 1200px) { | |
| margin-bottom: 0; | |
| flex: 1; | |
| } | |
| input, | |
| .ant-select { | |
| margin-top: auto; | |
| } | |
| &__input-container { | |
| display: flex; | |
| input { | |
| &:not(:last-child) { | |
| margin-right: 10px; | |
| } | |
| } | |
| } | |
| } | |
| &__label { | |
| font-weight: bold; | |
| } | |
| } | |
| .rule-action { | |
| margin-bottom: 10px; | |
| } | |
| .tags-modal { | |
| .ant-divider { | |
| margin-top: 0; | |
| } | |
| } | |
| .tags { | |
| margin-bottom: 10px; | |
| } | |
| .add-tags { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| &__input { | |
| margin-right: 10px; | |
| } | |
| &__label { | |
| margin-bottom: 5px; | |
| font-weight: bold; | |
| } | |
| } | |
| .tags-container { | |
| display: flex; | |
| flex-wrap: wrap; | |
| margin-bottom: 10px; | |
| } | |
| .add-tags-done { | |
| display: block; | |
| margin-left: auto; | |
| } | |
| .modal-loading { | |
| position: absolute; | |
| top: 0; | |
| right: 0; | |
| bottom: 0; | |
| left: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background-color: rgba(0,0,0,0.5); | |
| z-index: 1; | |
| color: #1890ff; | |
| font-size: 2rem; | |
| } | |
| .ant-list-item { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: flex-start; | |
| @media (min-width: 760px) { | |
| flex-direction: row; | |
| align-items: center; | |
| } | |
| } | |
| .rule-instance-collapse { | |
| width: 100%; | |
| margin-left: -15px; | |
| .ant-collapse-item { | |
| border: 0; | |
| } | |
| } | |
| .rule-instance-list { | |
| display: flex; | |
| flex-direction: column; | |
| &__item { | |
| display: flex; | |
| flex-wrap: wrap; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 10px; | |
| div { | |
| margin-left: 25px; | |
| margin-bottom: 10px; | |
| } | |
| } | |
| } | |
| .edit-rule { | |
| .ant-select { | |
| width: 100%; | |
| } | |
| &__item { | |
| margin-bottom: 10px; | |
| } | |
| &__label { | |
| margin-bottom: 5px; | |
| font-weight: bold; | |
| } | |
| } | |
| .vm-modal { | |
| &__header { | |
| display: flex; | |
| span { | |
| flex: 1; | |
| font-weight: bold; | |
| margin-right: 10px; | |
| } | |
| } | |
| &__item { | |
| display: flex; | |
| margin-top: 10px; | |
| span, | |
| label { | |
| display: block; | |
| flex: 1; | |
| margin-right: 10px; | |
| } | |
| } | |
| } | |
| .custom-ant-form { | |
| .ant-form-item-label { | |
| font-weight: bold; | |
| line-height: 1; | |
| } | |
| .ant-form-item { | |
| margin-bottom: 10px; | |
| } | |
| } | |
| .custom-ant-list { | |
| .ant-list-item-action { | |
| margin-top: 10px; | |
| margin-left: 0; | |
| @media (min-width: 760px) { | |
| margin-top: 0; | |
| margin-left: 24px; | |
| } | |
| } | |
| } | |
| .rule-instance-collapse { | |
| .ant-collapse-header, | |
| .ant-collapse-content { | |
| margin-left: -12px; | |
| } | |
| } | |
| .rule { | |
| .ant-list-item-content-single { | |
| width: 100%; | |
| @media (min-width: 760px) { | |
| width: auto; | |
| } | |
| } | |
| } | |
| .pagination { | |
| margin-top: 20px; | |
| text-align: right; | |
| } | |
| .actions { | |
| button { | |
| &:not(:last-child) { | |
| margin-right: 10px; | |
| } | |
| } | |
| } | |
| .list-view { | |
| overflow-y: auto; | |
| display: block; | |
| width: 100%; | |
| } | |
| .filter { | |
| display: block; | |
| width: 240px; | |
| margin-bottom: 10px; | |
| } | |
| .input-search { | |
| margin-bottom: 10px; | |
| width: 50%; | |
| float: right; | |
| } | |
| </style> |