mirror of https://github.com/apache/cloudstack.git
Merge branch '4.16' into main
This commit is contained in:
commit
f610e89ea9
|
|
@ -400,7 +400,7 @@ install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-inte
|
|||
|
||||
%preun management
|
||||
/usr/bin/systemctl stop cloudstack-management || true
|
||||
/usr/bin/systemctl off cloudstack-management || true
|
||||
/usr/bin/systemctl disable cloudstack-management || true
|
||||
|
||||
%pre management
|
||||
id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack unprivileged user" \
|
||||
|
|
@ -431,7 +431,7 @@ pip3 install %{_datadir}/%{name}-management/setup/wheel/six-1.15.0-py2.py3-none-
|
|||
|
||||
pip3 install urllib3
|
||||
|
||||
/usr/bin/systemctl on cloudstack-management > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
|
||||
|
||||
grep -s -q "db.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "db.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-inte
|
|||
|
||||
%preun management
|
||||
/usr/bin/systemctl stop cloudstack-management || true
|
||||
/usr/bin/systemctl off cloudstack-management || true
|
||||
/usr/bin/systemctl disable cloudstack-management || true
|
||||
|
||||
%pre management
|
||||
id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack unprivileged user" \
|
||||
|
|
@ -422,7 +422,7 @@ fi
|
|||
# Install mysql-connector-python
|
||||
pip3 install %{_datadir}/%{name}-management/setup/wheel/six-1.15.0-py2.py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/setuptools-47.3.1-py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/protobuf-3.12.2-cp36-cp36m-manylinux1_x86_64.whl %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.20-cp36-cp36m-manylinux1_x86_64.whl
|
||||
|
||||
/usr/bin/systemctl on cloudstack-management > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
|
||||
|
||||
grep -s -q "db.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "db.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ install -D tools/whisker/LICENSE ${RPM_BUILD_ROOT}%{_defaultdocdir}/%{name}-inte
|
|||
|
||||
%preun management
|
||||
/usr/bin/systemctl stop cloudstack-management || true
|
||||
/usr/bin/systemctl off cloudstack-management || true
|
||||
/usr/bin/systemctl disable cloudstack-management || true
|
||||
|
||||
%pre management
|
||||
id cloud > /dev/null 2>&1 || /usr/sbin/useradd -M -U -c "CloudStack unprivileged user" \
|
||||
|
|
@ -424,7 +424,7 @@ fi
|
|||
# Install mysql-connector-python
|
||||
pip3 install %{_datadir}/%{name}-management/setup/wheel/six-1.15.0-py2.py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/setuptools-47.3.1-py3-none-any.whl %{_datadir}/%{name}-management/setup/wheel/protobuf-3.12.2-cp36-cp36m-manylinux1_x86_64.whl %{_datadir}/%{name}-management/setup/wheel/mysql_connector_python-8.0.20-cp36-cp36m-manylinux1_x86_64.whl
|
||||
|
||||
/usr/bin/systemctl on cloudstack-management > /dev/null 2>&1 || true
|
||||
/usr/bin/systemctl enable cloudstack-management > /dev/null 2>&1 || true
|
||||
|
||||
grep -s -q "db.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.cloud.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
grep -s -q "db.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties" || sed -i -e "\$adb.usage.driver=jdbc:mysql" "%{_sysconfdir}/%{name}/management/db.properties"
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class AddNetscalerLoadBalancerCmd extends BaseAsyncCmd {
|
|||
@Parameter(name = ApiConstants.GSLB_PROVIDER_PUBLIC_IP, type = CommandType.STRING, required = false, description = "public IP of the site")
|
||||
private String gslbSitePublicIp;
|
||||
|
||||
@Parameter(name = ApiConstants.GSLB_PROVIDER_PRIVATE_IP, type = CommandType.STRING, required = false, description = "public IP of the site")
|
||||
@Parameter(name = ApiConstants.GSLB_PROVIDER_PRIVATE_IP, type = CommandType.STRING, required = false, description = "private IP of the site")
|
||||
private String gslbSitePrivateIp;
|
||||
|
||||
@Parameter(name = ApiConstants.EXCLUSIVE_GSLB_PROVIDER,
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocationException {
|
||||
TemplateProfile profile = super.prepare(cmd);
|
||||
String url = profile.getUrl();
|
||||
UriUtils.validateUrl(cmd.getFormat(), url);
|
||||
UriUtils.validateUrl(cmd.getFormat(), url, cmd.isDirectDownload());
|
||||
if (cmd.isDirectDownload()) {
|
||||
DigestHelper.validateChecksumString(cmd.getChecksum());
|
||||
Long templateSize = performDirectDownloadUrlValidation(cmd.getFormat(), url, cmd.getZoneIds());
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
</a-affix>
|
||||
|
||||
<div v-show="showAction">
|
||||
<keep-alive v-if="currentAction.component && (!currentAction.groupAction || this.selectedRowKeys.length === 0)">
|
||||
<keep-alive v-if="currentAction.component && (!currentAction.groupAction || this.selectedRowKeys.length === 0 || (this.selectedRowKeys.length > 0 && currentAction.api === 'destroyVirtualMachine'))">
|
||||
<a-modal
|
||||
:visible="showAction"
|
||||
:closable="true"
|
||||
|
|
@ -131,10 +131,14 @@
|
|||
:resource="resource"
|
||||
:loading="loading"
|
||||
:action="{currentAction}"
|
||||
:selectedRowKeys="selectedRowKeys"
|
||||
:selectedItems="selectedItems"
|
||||
:chosenColumns="chosenColumns"
|
||||
v-bind="{currentAction}"
|
||||
@refresh-data="fetchData"
|
||||
@poll-action="pollActionCompletion"
|
||||
@close-action="closeAction"/>
|
||||
@close-action="closeAction"
|
||||
@cancel-bulk-action="handleCancel"/>
|
||||
</a-modal>
|
||||
</keep-alive>
|
||||
<a-modal
|
||||
|
|
@ -913,8 +917,10 @@ export default {
|
|||
}
|
||||
}
|
||||
if (this.items.length > 0) {
|
||||
this.resource = this.items[0]
|
||||
this.$emit('change-resource', this.resource)
|
||||
if (!this.showAction) {
|
||||
this.resource = this.items[0]
|
||||
this.$emit('change-resource', this.resource)
|
||||
}
|
||||
} else {
|
||||
if (this.dataView) {
|
||||
this.$router.push({ path: '/exception/404' })
|
||||
|
|
@ -1594,4 +1600,9 @@ export default {
|
|||
.ant-breadcrumb {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
/deep/.ant-alert-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -16,67 +16,155 @@
|
|||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||
<a-alert type="warning" v-html="resource.backupofferingid ? $t('message.action.destroy.instance.with.backups') : $t('message.action.destroy.instance')" /><br/>
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item v-if="$store.getters.userInfo.roletype === 'Admin' || $store.getters.features.allowuserexpungerecovervm">
|
||||
<tooltip-label slot="label" :title="$t('label.expunge')" :tooltip="apiParams.expunge.description"/>
|
||||
<a-switch v-decorator="['expunge']" :auto-focus="true" />
|
||||
</a-form-item>
|
||||
<div :class="['form-layout', { 'form-list': selectedRowKeys.length > 0 }]" v-ctrl-enter="handleSubmit">
|
||||
<div v-if="selectedRowKeys.length === 0">
|
||||
<a-alert type="warning" v-html="resource.backupofferingid ? $t('message.action.destroy.instance.with.backups') : $t('message.action.destroy.instance')" /><br/>
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item v-if="$store.getters.userInfo.roletype === 'Admin' || $store.getters.features.allowuserexpungerecovervm">
|
||||
<tooltip-label slot="label" :title="$t('label.expunge')" :tooltip="apiParams.expunge.description"/>
|
||||
<a-switch v-decorator="['expunge']" :auto-focus="true" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="volumes.length > 0">
|
||||
<tooltip-label slot="label" :title="$t('label.delete.volumes')" :tooltip="apiParams.volumeids.description"/>
|
||||
<a-select
|
||||
v-decorator="['volumeids']"
|
||||
:placeholder="$t('label.delete.volumes')"
|
||||
mode="multiple"
|
||||
:loading="loading"
|
||||
:autoFocus="$store.getters.userInfo.roletype !== 'Admin' && !$store.getters.features.allowuserexpungerecovervm"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}" >
|
||||
<a-select-option v-for="volume in volumes" :key="volume.id">
|
||||
{{ volume.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<p v-else v-html="$t('label.volume.empty')" />
|
||||
<a-form-item v-if="volumes.length > 0">
|
||||
<tooltip-label slot="label" :title="$t('label.delete.volumes')" :tooltip="apiParams.volumeids.description"/>
|
||||
<a-select
|
||||
v-decorator="['volumeids']"
|
||||
:placeholder="$t('label.delete.volumes')"
|
||||
mode="multiple"
|
||||
:loading="loading"
|
||||
:autoFocus="$store.getters.userInfo.roletype !== 'Admin' && !$store.getters.features.allowuserexpungerecovervm"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}" >
|
||||
<a-select-option v-for="volume in volumes" :key="volume.id">
|
||||
{{ volume.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<p v-else v-html="$t('label.volume.empty')" />
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="!showGroupActionModal">
|
||||
<div>
|
||||
<a-alert type="error">
|
||||
<a-icon slot="message" type="exclamation-circle" style="color: red; fontSize: 30px; display: inline-flex" />
|
||||
<span style="padding-left: 5px" slot="message" v-html="`<b>${selectedRowKeys.length} ` + $t('label.items.selected') + `. </b>`" />
|
||||
<span slot="message" v-html="$t(action.currentAction.message)" />
|
||||
</a-alert>
|
||||
</div>
|
||||
<div v-if="selectedRowKeys.length > 0" class="row-keys">
|
||||
<a-divider />
|
||||
<a-table
|
||||
v-if="selectedRowKeys.length > 0"
|
||||
size="middle"
|
||||
:columns="chosenColumns"
|
||||
:dataSource="selectedItems"
|
||||
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||
:pagination="true"
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
<p
|
||||
slot="expandedRowRender"
|
||||
slot-scope="record"
|
||||
style="margin: 0">
|
||||
<a-form-item :label="$t('label.delete.volumes')" v-if="listVolumes[record.id].opts.length > 0">
|
||||
<a-select
|
||||
mode="multiple"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="listVolumes[record.id].loading"
|
||||
:placeholder="$t('label.delete.volumes')"
|
||||
@change="(value) => onChangeVolume(record.id, value)">
|
||||
<a-select-option v-for="item in listVolumes[record.id].opts" :key="item.id">
|
||||
{{ item.name || item.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<span v-else v-html="$t('label.volume.empty')" />
|
||||
</p>
|
||||
</a-table>
|
||||
<a-form-item v-if="$store.getters.userInfo.roletype === 'Admin' || $store.getters.features.allowuserexpungerecovervm">
|
||||
<tooltip-label slot="label" :title="$t('label.expunge')" :tooltip="apiParams.expunge.description"/>
|
||||
<a-switch v-model="expunge" :auto-focus="true" />
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</div>
|
||||
<bulk-action-progress
|
||||
:showGroupActionModal="showGroupActionModal"
|
||||
:selectedItems="selectedItemsProgress"
|
||||
:selectedColumns="selectedColumns"
|
||||
:message="modalInfo"
|
||||
@handle-cancel="handleCancel" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||
import BulkActionProgress from '@/components/view/BulkActionProgress'
|
||||
|
||||
export default {
|
||||
name: 'DestroyVM',
|
||||
components: {
|
||||
TooltipLabel
|
||||
TooltipLabel,
|
||||
BulkActionProgress
|
||||
},
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
action: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
selectedRowKeys: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
selectedItems: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
chosenColumns: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData'],
|
||||
data () {
|
||||
return {
|
||||
volumes: [],
|
||||
loading: false
|
||||
loading: false,
|
||||
volumeIds: {},
|
||||
listVolumes: {},
|
||||
selectedColumns: [],
|
||||
selectedItemsProgress: [],
|
||||
showGroupActionModal: false,
|
||||
modalInfo: {},
|
||||
expunge: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
|
|
@ -88,66 +176,198 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.volumes = []
|
||||
if (this.selectedRowKeys.length === 0) {
|
||||
this.fetchVolumes()
|
||||
} else {
|
||||
const promises = []
|
||||
this.selectedRowKeys.forEach(vmId => {
|
||||
this.listVolumes[vmId] = {
|
||||
loading: true,
|
||||
opts: []
|
||||
}
|
||||
promises.push(this.callListVolume(vmId))
|
||||
})
|
||||
Promise.all(promises).then((data) => {
|
||||
data.forEach(item => {
|
||||
this.listVolumes[item.id].loading = false
|
||||
this.listVolumes[item.id].opts = item.volumes || []
|
||||
})
|
||||
this.$forceUpdate()
|
||||
})
|
||||
}
|
||||
},
|
||||
async fetchVolumes () {
|
||||
this.loading = true
|
||||
api('listVolumes', {
|
||||
virtualMachineId: this.resource.id,
|
||||
type: 'DATADISK',
|
||||
details: 'min',
|
||||
listall: 'true'
|
||||
}).then(json => {
|
||||
this.volumes = json.listvolumesresponse.volume || []
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
const data = await this.callListVolume(this.resource.id)
|
||||
this.volumes = data.volumes || []
|
||||
this.loading = false
|
||||
},
|
||||
callListVolume (vmId) {
|
||||
return new Promise((resolve) => {
|
||||
this.volumes = []
|
||||
api('listVolumes', {
|
||||
virtualMachineId: vmId,
|
||||
type: 'DATADISK',
|
||||
details: 'min',
|
||||
listall: 'true'
|
||||
}).then(json => {
|
||||
const volumes = json.listvolumesresponse.volume || []
|
||||
resolve({
|
||||
id: vmId,
|
||||
volumes
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
onChangeVolume (vmId, volumes) {
|
||||
this.volumeIds[vmId] = volumes
|
||||
},
|
||||
handleCancel () {
|
||||
this.$emit('cancel-bulk-action')
|
||||
this.showGroupActionModal = false
|
||||
this.selectedItemsProgress = []
|
||||
this.selectedColumns = []
|
||||
this.closeAction()
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
this.form.validateFieldsAndScroll((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
|
||||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
if (values.volumeids) {
|
||||
params.volumeids = values.volumeids.join(',')
|
||||
}
|
||||
if (values.expunge) {
|
||||
params.expunge = values.expunge
|
||||
}
|
||||
|
||||
if (this.selectedRowKeys.length > 0) {
|
||||
this.destroyGroupVMs()
|
||||
} else {
|
||||
this.form.validateFieldsAndScroll(async (err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
if (values.volumeids) {
|
||||
params.volumeids = values.volumeids.join(',')
|
||||
}
|
||||
if (values.expunge) {
|
||||
params.expunge = values.expunge
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
const jobId = await this.destroyVM(params)
|
||||
await this.$pollJob({
|
||||
jobId,
|
||||
title: this.$t('label.action.destroy.instance'),
|
||||
description: this.resource.name,
|
||||
loadingMessage: `${this.$t('message.deleting.vm')} ${this.resource.name}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
successMessage: `${this.$t('message.success.delete.vm')} ${this.resource.name}`,
|
||||
successMethod: () => {
|
||||
if (this.$route.path.includes('/vm/') && values.expunge) {
|
||||
this.$router.go(-1)
|
||||
} else {
|
||||
this.parentFetchData()
|
||||
}
|
||||
},
|
||||
action: {
|
||||
isFetchData: false
|
||||
}
|
||||
})
|
||||
await this.closeAction()
|
||||
this.loading = false
|
||||
} catch (error) {
|
||||
await this.$notifyError(error)
|
||||
await this.closeAction()
|
||||
this.loading = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
destroyVM (params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
api('destroyVirtualMachine', params).then(json => {
|
||||
const jobId = json.destroyvirtualmachineresponse.jobid
|
||||
return resolve(jobId)
|
||||
}).catch(error => {
|
||||
return reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyGroupVMs () {
|
||||
this.selectedColumns = Array.from(this.chosenColumns)
|
||||
this.selectedItemsProgress = Array.from(this.selectedItems)
|
||||
this.selectedItemsProgress = this.selectedItemsProgress.map(v => ({ ...v, status: 'InProgress' }))
|
||||
this.selectedColumns.splice(0, 0, {
|
||||
dataIndex: 'status',
|
||||
title: this.$t('label.operation.status'),
|
||||
scopedSlots: { customRender: 'status' },
|
||||
filters: [
|
||||
{ text: 'In Progress', value: 'InProgress' },
|
||||
{ text: 'Success', value: 'success' },
|
||||
{ text: 'Failed', value: 'failed' }
|
||||
]
|
||||
})
|
||||
this.showGroupActionModal = true
|
||||
this.modalInfo.title = this.action.currentAction.label
|
||||
this.modalInfo.docHelp = this.action.currentAction.docHelp
|
||||
const promises = []
|
||||
this.selectedRowKeys.forEach(vmId => {
|
||||
const params = {}
|
||||
params.id = vmId
|
||||
if (this.volumeIds[vmId] && this.volumeIds[vmId].length > 0) {
|
||||
params.volumeids = this.volumeIds[vmId].join(',')
|
||||
}
|
||||
if (this.expunge) {
|
||||
params.expunge = this.expunge
|
||||
}
|
||||
promises.push(this.callGroupApi(params))
|
||||
})
|
||||
this.$message.info({
|
||||
content: this.$t(this.action.currentAction.label),
|
||||
key: this.action.currentAction.label,
|
||||
duration: 3
|
||||
})
|
||||
this.loading = true
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loading = false
|
||||
this.parentFetchData()
|
||||
})
|
||||
},
|
||||
callGroupApi (params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const resource = this.selectedItems.filter(item => item.id === params.id)[0] || {}
|
||||
this.destroyVM(params).then(jobId => {
|
||||
this.updateResourceState(resource.id, 'InProgress', jobId)
|
||||
this.$pollJob({
|
||||
jobId,
|
||||
showLoading: false,
|
||||
bulkAction: false,
|
||||
title: this.$t('label.action.destroy.instance'),
|
||||
description: this.resource.name,
|
||||
loadingMessage: `${this.$t('message.deleting.vm')} ${this.resource.name}`,
|
||||
catchMessage: this.$t('error.fetching.async.job.result'),
|
||||
successMessage: `${this.$t('message.success.delete.vm')} ${this.resource.name}`,
|
||||
successMessage: `${this.$t('message.success.delete.vm')} ${resource.name}`,
|
||||
successMethod: () => {
|
||||
if (this.$route.path.includes('/vm/') && values.expunge) {
|
||||
this.$router.go(-1)
|
||||
} else {
|
||||
this.parentFetchData()
|
||||
}
|
||||
this.updateResourceState(resource.id, 'success')
|
||||
return resolve()
|
||||
},
|
||||
errorMethod: () => {
|
||||
this.updateResourceState(resource.id, 'failed')
|
||||
},
|
||||
action: {
|
||||
isFetchData: false
|
||||
}
|
||||
})
|
||||
this.closeAction()
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
this.updateResourceState(resource.id, 'failed')
|
||||
return reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
updateResourceState (resource, state, jobId) {
|
||||
const objIndex = this.selectedItemsProgress.findIndex(item => item.id === resource)
|
||||
if (state && objIndex !== -1) {
|
||||
this.selectedItemsProgress[objIndex].status = state
|
||||
}
|
||||
if (jobId && objIndex !== -1) {
|
||||
this.selectedItemsProgress[objIndex].jobid = jobId
|
||||
}
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
|
|
@ -157,10 +377,23 @@ export default {
|
|||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 60vw;
|
||||
&.form-list {
|
||||
max-width: 60vw;
|
||||
}
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 450px;
|
||||
&:not(.form-list) {
|
||||
width: 60vw;
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.row-keys {
|
||||
.ant-select {
|
||||
display: block;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.ip')">
|
||||
<a-input
|
||||
:placeholder="apiParams.url.description"
|
||||
autoFocus
|
||||
v-decorator="['ip', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.username')">
|
||||
<a-input
|
||||
:placeholder="apiParams.username.description"
|
||||
v-decorator="['username', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.password')">
|
||||
<a-input-password
|
||||
:placeholder="apiParams.password.description"
|
||||
v-decorator="['password', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -56,6 +59,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.networkdevicetype')">
|
||||
<a-select
|
||||
:placeholder="apiParams.networkdevicetype.description"
|
||||
v-decorator="['networkdevicetype', {
|
||||
rules: [{ required: true, message: $t('message.error.select') }]
|
||||
}]"
|
||||
|
|
@ -135,6 +139,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
apiParams: {},
|
||||
loading: false,
|
||||
nsp: {}
|
||||
}
|
||||
|
|
@ -153,6 +158,9 @@ export default {
|
|||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addF5LoadBalancer')
|
||||
},
|
||||
mounted () {
|
||||
if (this.resource && Object.keys(this.resource).length > 0) {
|
||||
this.nsp = this.resource
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.ip')">
|
||||
<a-input
|
||||
:placeholder="apiParams.url.description"
|
||||
autoFocus
|
||||
v-decorator="['ip', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.username')">
|
||||
<a-input
|
||||
:placeholder="apiParams.username.description"
|
||||
v-decorator="['username', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.password')">
|
||||
<a-input-password
|
||||
:placeholder="apiParams.password.description"
|
||||
v-decorator="['password', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -56,6 +59,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.networkdevicetype')">
|
||||
<a-select
|
||||
:placeholder="apiParams.networkdevicetype.description"
|
||||
v-decorator="['networkdevicetype', {
|
||||
rules: [{ required: true, message: $t('message.error.select') }]
|
||||
}]"
|
||||
|
|
@ -91,6 +95,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.gslbprovider')">
|
||||
<a-switch
|
||||
:placeholder="apiParams.gslbprovider.description"
|
||||
v-decorator="['gslbprovider', { initialValue: false }]" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -99,6 +104,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.gslbproviderpublicip')">
|
||||
<a-input
|
||||
:placeholder="apiParams.gslbproviderpublicip.description"
|
||||
v-decorator="['gslbproviderpublicip']" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -107,6 +113,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.gslbproviderprivateip')">
|
||||
<a-input
|
||||
:placeholder="apiParams.gslbproviderprivateip.description"
|
||||
v-decorator="['gslbproviderprivateip']" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -159,6 +166,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
apiParams: {},
|
||||
loading: false,
|
||||
nsp: {}
|
||||
}
|
||||
|
|
@ -185,6 +193,9 @@ export default {
|
|||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addNetscalerLoadBalancer')
|
||||
},
|
||||
mounted () {
|
||||
if (this.resource && Object.keys(this.resource).length > 0) {
|
||||
this.nsp = this.resource
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<a-form-item :label="$t('label.ip')">
|
||||
<a-input
|
||||
autoFocus
|
||||
:placeholder="apiParams.hostname.description"
|
||||
v-decorator="['ip', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.username')">
|
||||
<a-input
|
||||
:placeholder="apiParams.username.description"
|
||||
v-decorator="['username', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.password')">
|
||||
<a-input-password
|
||||
:placeholder="apiParams.password.description"
|
||||
v-decorator="['password', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -65,6 +68,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.transportzoneuuid')">
|
||||
<a-input
|
||||
:placeholder="apiParams.transportzoneuuid.description"
|
||||
v-decorator="['transportzoneuuid']" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -73,6 +77,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.l3gatewayserviceuuid')">
|
||||
<a-input
|
||||
:placeholder="apiParams.l3gatewayserviceuuid.description"
|
||||
v-decorator="['l3gatewayserviceuuid']" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -81,6 +86,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.l2gatewayserviceuuid')">
|
||||
<a-input
|
||||
:placeholder="apiParams.l2gatewayserviceuuid.description"
|
||||
v-decorator="['l2gatewayserviceuuid']" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
@ -110,6 +116,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
apiParams: {},
|
||||
loading: false,
|
||||
nsp: {}
|
||||
}
|
||||
|
|
@ -117,6 +124,9 @@ export default {
|
|||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addNiciraNvpDevice')
|
||||
},
|
||||
mounted () {
|
||||
if (this.resource && Object.keys(this.resource).length > 0) {
|
||||
this.nsp = this.resource
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
<a-form-item :label="$t('label.ip')">
|
||||
<a-input
|
||||
autoFocus
|
||||
:placeholder="apiParams.url.description"
|
||||
v-decorator="['ip', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.username')">
|
||||
<a-input
|
||||
:placeholder="apiParams.username.description"
|
||||
v-decorator="['username', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.password')">
|
||||
<a-input-password
|
||||
:placeholder="apiParams.password.description"
|
||||
v-decorator="['password', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -56,6 +59,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.networkdevicetype')">
|
||||
<a-select
|
||||
:placeholder="apiParams.networkdevicetype.description"
|
||||
v-decorator="['networkdevicetype', {
|
||||
rules: [{ required: true, message: $t('message.error.select') }]
|
||||
}]"
|
||||
|
|
@ -182,6 +186,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
apiParams: {},
|
||||
loading: false,
|
||||
nsp: {}
|
||||
}
|
||||
|
|
@ -200,6 +205,9 @@ export default {
|
|||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addPaloAltoFirewall')
|
||||
},
|
||||
mounted () {
|
||||
if (this.resource && Object.keys(this.resource).length > 0) {
|
||||
this.nsp = this.resource
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.ip')">
|
||||
<a-input
|
||||
:placeholder="apiParams.url.description"
|
||||
autoFocus
|
||||
v-decorator="['ip', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.username')">
|
||||
<a-input
|
||||
:placeholder="apiParams.username.description"
|
||||
v-decorator="['username', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -46,6 +48,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.password')">
|
||||
<a-input-password
|
||||
:placeholder="apiParams.password.description"
|
||||
v-decorator="['password', {
|
||||
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||
}]" />
|
||||
|
|
@ -56,6 +59,7 @@
|
|||
<a-col :md="24" :lg="24">
|
||||
<a-form-item :label="$t('label.networkdevicetype')">
|
||||
<a-select
|
||||
:placeholder="apiParams.networkdevicetype.description"
|
||||
v-decorator="['networkdevicetype', {
|
||||
rules: [{ required: true, message: $t('message.error.select') }]
|
||||
}]"
|
||||
|
|
@ -160,6 +164,7 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
apiParams: {},
|
||||
loading: false,
|
||||
nsp: {}
|
||||
}
|
||||
|
|
@ -178,6 +183,9 @@ export default {
|
|||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
created () {
|
||||
this.apiParams = this.$getApiParams('addSrxFirewall')
|
||||
},
|
||||
mounted () {
|
||||
if (this.resource && Object.keys(this.resource).length > 0) {
|
||||
this.nsp = this.resource
|
||||
|
|
|
|||
|
|
@ -263,6 +263,10 @@ public class UriUtils {
|
|||
}
|
||||
|
||||
public static Pair<String, Integer> validateUrl(String format, String url) throws IllegalArgumentException {
|
||||
return validateUrl(format, url, false);
|
||||
}
|
||||
|
||||
public static Pair<String, Integer> validateUrl(String format, String url, boolean skipIpv6Check) throws IllegalArgumentException {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
if ((uri.getScheme() == null) ||
|
||||
|
|
@ -283,7 +287,7 @@ public class UriUtils {
|
|||
if (hostAddr.isAnyLocalAddress() || hostAddr.isLinkLocalAddress() || hostAddr.isLoopbackAddress() || hostAddr.isMulticastAddress()) {
|
||||
throw new IllegalArgumentException("Illegal host specified in url");
|
||||
}
|
||||
if (hostAddr instanceof Inet6Address) {
|
||||
if (!skipIpv6Check && hostAddr instanceof Inet6Address) {
|
||||
throw new IllegalArgumentException("IPV6 addresses not supported (" + hostAddr.getHostAddress() + ")");
|
||||
}
|
||||
} catch (UnknownHostException uhe) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue