Merge branch '4.16' into main

This commit is contained in:
Suresh Kumar Anaparti 2022-01-28 13:43:00 +05:30
commit f610e89ea9
No known key found for this signature in database
GPG Key ID: D7CEAE3A9E71D0AA
13 changed files with 380 additions and 87 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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,

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {