infra: Add support to manage network service providers (#252)

Fixes #250

 - Add support for all the network service providers which are not already supported
 - Each NSP must be implemented in its separate component
 - For each NSP component, add buttons and their forms in a modal and display a list of such NSP devices/resources in a table with pagination

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Hoang Nguyen 2020-07-01 15:34:12 +07:00 committed by Rohit Yadav
parent fdfa38ac78
commit 360bc5f050
12 changed files with 3773 additions and 79 deletions

View File

@ -800,6 +800,7 @@
"label.extra": "Extra Arguments",
"label.f5": "F5",
"label.f5.details": "F5 details",
"label.f5.ip.loadbalancer": "F5 Big Ip Load Balancer",
"label.failed": "Failed",
"label.featured": "Featured",
"label.fetch.latest": "Fetch latest",
@ -1127,6 +1128,8 @@
"label.limits": "Limits",
"label.link.domain.to.ldap": "Link Domain to LDAP",
"label.linklocalip": "Link Local IP Address",
"label.list.ciscovnmc": "Cisco VNMC",
"label.list.ciscoasa1000v": "ASA 1000v",
"label.load.balancer": "Load Balancer",
"label.load.balancing.policies": "Load balancing policies",
"label.loadbalancerinstance": "Assigned VMs",
@ -1274,6 +1277,9 @@
"label.netmask": "Netmask",
"label.netscaler": "NetScaler",
"label.netscaler.details": "NetScaler details",
"label.netscaler.mpx": "NetScaler MPX LoadBalancer",
"label.netscaler.sdx": "NetScaler SDX LoadBalancer",
"label.netscaler.vpx": "NetScaler VPX LoadBalancer",
"label.network": "Network",
"label.network.acl": "Network ACL",
"label.network.acl.lists": "Network ACL Lists",
@ -1398,6 +1404,7 @@
"label.owner.domain": "Owner Domain",
"label.pa": "Palo Alto",
"label.palo.alto.details": "Palo Alto details",
"label.palo.alto.firewall": "Palo Alto Firewall",
"label.palp": "Palo Alto Log Profile",
"label.params": "Parameters",
"label.parent.domain": "Parent Domain",
@ -1794,6 +1801,7 @@
"label.sr.name": "SR Name-Label",
"label.srx": "SRX",
"label.srx.details": "SRX details",
"label.srx.firewall": "Juniper SRX Firewall",
"label.ssh.key.pair.details": "SSH Key Pair Details",
"label.ssh.key.pairs": "SSH Key Pairs",
"label.sshkeyenabled": "SSH Enabled",
@ -2315,6 +2323,7 @@
"message.confirm.delete.kubernetes.version": "Please confirm that you want to delete this Kubernetes version.",
"message.confirm.delete.netscaler": "Please confirm that you would like to delete NetScaler",
"message.confirm.delete.pa": "Please confirm that you would like to delete Palo Alto",
"message.confirm.delete.provider": "Please confirm that you would like to delete this provider?",
"message.confirm.delete.secondary.staging.store": "Please confirm you want to delete Secondary Staging Store.",
"message.confirm.delete.srx": "Please confirm that you would like to delete SRX",
"message.confirm.delete.ucs.manager": "Please confirm that you want to delete UCS Manager",
@ -2400,6 +2409,7 @@
"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers, and we will add the first one now. Primary storage contains the disk volumes for all the VMs running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.",
"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you would like to add to this VM. Please note the root password will be changed by this operation if password is enabled.",
"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server, and we will add the first one now. Secondary storage stores VM templates, ISO images, and VM disk volume snapshots. This server must be available to all hosts in the zone.<br/><br/>Provide the IP address and exported path.",
"message.confirm.delete.niciranvp":"Please confirm you want to delete Nicira Nvp Controller",
"message.desc.zone": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.",
"message.detach.disk": "Are you sure you want to detach this disk?",
"message.detach.iso.confirm": "Please confirm that you want to detach the ISO from this virtual instance.",

View File

@ -341,7 +341,8 @@ export default {
parentFetchData: this.fetchData,
parentToggleLoading: this.toggleLoading,
parentStartLoading: this.startLoading,
parentFinishLoading: this.finishLoading
parentFinishLoading: this.finishLoading,
parentPollActionCompletion: this.pollActionCompletion
}
},
data () {
@ -924,6 +925,9 @@ export default {
this.pageSize = pageSize
this.fetchData()
},
changeResource (resource) {
this.resource = resource
},
start () {
this.loading = true
this.fetchData()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,321 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="form-layout">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmit">
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.ip')">
<a-input
v-decorator="['ip', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.username')">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.password')">
<a-input-password
v-decorator="['password', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.networkdevicetype')">
<a-select
v-decorator="['networkdevicetype', {
rules: [{ required: true, message: $t('message.error.select') }]
}]">
<a-select-option
v-for="opt in networkDeviceType"
:key="opt.id">{{ $t(opt.description) }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.publicinterface')">
<a-input
v-decorator="['publicinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.privateinterface')">
<a-input
v-decorator="['privateinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.numretries')">
<a-input-number
style="width: 100%"
v-decorator="['numretries', { initialValue: 2 }]" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.dedicated')">
<a-switch
v-decorator="['dedicated', { initialValue: false }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.capacity')">
<a-input
v-decorator="['capacity']" />
</a-form-item>
</a-col>
</a-row>
<div :span="24" class="action-button">
<a-button :loading="loading" @click="onCloseAction">{{ this.$t('Cancel') }}</a-button>
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { api } from '@/api'
export default {
name: 'AddF5LoadBalancer',
props: {
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
loading: false,
nsp: {}
}
},
computed: {
networkDeviceType () {
const items = []
items.push({
id: 'F5BigIpLoadBalancer',
description: 'label.f5.ip.loadbalancer'
})
return items
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
mounted () {
if (this.resource && Object.keys(this.resource).length > 0) {
this.nsp = this.resource
}
},
inject: ['provideCloseAction', 'provideReload', 'provideCloseAction', 'parentPollActionCompletion'],
methods: {
onCloseAction () {
this.provideCloseAction()
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields(async (err, values) => {
if (err) {
return
}
const params = {}
params.physicalnetworkid = this.resource.physicalnetworkid
params.username = values.username
params.password = values.password
params.networkdevicetype = values.networkdevicetype
const url = []
const ip = values.ip
url.push('https://' + ip)
let isQuestionMarkAdded = false
const publicInterface = values.publicinterface ? values.publicinterface : null
if (publicInterface != null && publicInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicinterface=' + publicInterface)
}
const privateInterface = values.privateinterface ? values.privateinterface : null
if (privateInterface != null && privateInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privateinterface=' + publicInterface)
}
const numretries = values.numretries ? values.numretries : null
if (numretries != null && numretries.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('numretries=' + numretries)
}
const capacity = values.capacity ? values.capacity : null
if (capacity != null && capacity.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicecapacity=' + capacity)
}
const dedicated = values.dedicated ? values.dedicated : false
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicededicated=' + dedicated)
params.url = url.join('')
this.loading = true
try {
if (!this.nsp.id) {
const addParams = {}
addParams.name = this.resource.name
addParams.physicalnetworkid = this.resource.physicalnetworkid
const networkServiceProvider = await this.addNetworkServiceProvider(addParams)
this.nsp = { ...this.resource, ...networkServiceProvider }
}
params.id = this.nsp.id
const jobId = await this.addF5LoadBalancer(params)
if (jobId) {
await this.$store.dispatch('AddAsyncJob', {
title: this.$t(this.action.label),
jobid: jobId,
description: this.$t(this.nsp.name),
status: 'progress'
})
await this.parentPollActionCompletion(jobId, this.action)
}
this.loading = false
await this.provideCloseAction()
} catch (error) {
this.loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
})
},
addNetworkServiceProvider (args) {
return new Promise((resolve, reject) => {
api('addNetworkServiceProvider', args).then(async json => {
const jobId = json.addnetworkserviceproviderresponse.jobid
if (jobId) {
const result = await this.pollJob(jobId)
if (result.jobstatus === 2) {
reject(result.jobresult.errortext)
return
}
resolve(result.jobresult.networkserviceprovider)
}
}).catch(error => {
reject(error)
})
})
},
addF5LoadBalancer (args) {
return new Promise((resolve, reject) => {
api('addF5LoadBalancer', args).then(json => {
const jobId = json.addf5bigiploadbalancerresponse.jobid || null
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
async pollJob (jobId) {
return new Promise(resolve => {
const asyncJobInterval = setInterval(() => {
api('queryAsyncJobResult', { jobId }).then(async json => {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 0) {
return
}
clearInterval(asyncJobInterval)
resolve(result)
})
}, 1000)
})
}
}
}
</script>
<style scoped lang="less">
.form-layout {
.action-button {
text-align: right;
margin-top: 20px;
button {
margin-right: 5px;
}
}
}
</style>

View File

@ -0,0 +1,356 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="form-layout">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmit">
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.ip')">
<a-input
v-decorator="['ip', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.username')">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.password')">
<a-input-password
v-decorator="['password', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.networkdevicetype')">
<a-select
v-decorator="['networkdevicetype', {
rules: [{ required: true, message: $t('message.error.select') }]
}]">
<a-select-option
v-for="opt in networkDeviceType"
:key="opt.id">{{ $t(opt.description) }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.publicinterface')">
<a-input
v-decorator="['publicinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.privateinterface')">
<a-input
v-decorator="['privateinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.gslbprovider')">
<a-switch
v-decorator="['gslbprovider', { initialValue: false }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.gslbproviderpublicip')">
<a-input
v-decorator="['gslbproviderpublicip']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.gslbproviderprivateip')">
<a-input
v-decorator="['gslbproviderprivateip']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.numretries')">
<a-input-number
style="width: 100%"
v-decorator="['numretries', { initialValue: 2 }]" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.dedicated')">
<a-switch
v-decorator="['dedicated', { initialValue: false }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.capacity')">
<a-input
v-decorator="['capacity']" />
</a-form-item>
</a-col>
</a-row>
<div :span="24" class="action-button">
<a-button :loading="loading" @click="onCloseAction">{{ this.$t('Cancel') }}</a-button>
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { api } from '@/api'
export default {
name: 'AddNetscalerLoadBalancer',
props: {
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
loading: false,
nsp: {}
}
},
computed: {
networkDeviceType () {
const items = []
items.push({
id: 'NetscalerMPXLoadBalancer',
description: 'label.netscaler.mpx'
})
items.push({
id: 'NetscalerVPXLoadBalancer',
description: 'label.netscaler.vpx'
})
items.push({
id: 'NetscalerSDXLoadBalancer',
description: 'label.netscaler.sdx'
})
return items
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
mounted () {
if (this.resource && Object.keys(this.resource).length > 0) {
this.nsp = this.resource
}
},
inject: ['provideCloseAction', 'provideReload', 'provideCloseAction', 'parentPollActionCompletion'],
methods: {
onCloseAction () {
this.provideCloseAction()
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields(async (err, values) => {
if (err) {
return
}
const params = {}
params.physicalnetworkid = this.resource.physicalnetworkid
params.username = values.username
params.password = values.password
params.networkdevicetype = values.networkdevicetype
params.gslbprovider = values.gslbprovider ? values.gslbprovider : false
params.gslbproviderpublicip = values.gslbproviderpublicip ? values.gslbproviderpublicip : null
params.gslbproviderprivateip = values.gslbproviderprivateip ? values.gslbproviderprivateip : null
const url = []
const ip = values.ip
url.push('https://' + ip)
let isQuestionMarkAdded = false
const publicInterface = values.publicinterface ? values.publicinterface : null
if (publicInterface != null && publicInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicinterface=' + publicInterface)
}
const privateInterface = values.privateinterface ? values.privateinterface : null
if (privateInterface != null && privateInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privateinterface=' + publicInterface)
}
const numretries = values.numretries ? values.numretries : null
if (numretries != null && numretries.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('numretries=' + numretries)
}
const capacity = values.capacity ? values.capacity : null
if (capacity != null && capacity.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicecapacity=' + capacity)
}
const dedicated = values.dedicated ? values.dedicated : false
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicededicated=' + dedicated)
params.url = url.join('')
this.loading = true
try {
if (!this.nsp.id) {
const addParams = {}
addParams.name = this.resource.name
addParams.physicalnetworkid = this.resource.physicalnetworkid
const networkServiceProvider = await this.addNetworkServiceProvider(addParams)
this.nsp = { ...this.resource, ...networkServiceProvider }
}
params.id = this.nsp.id
const jobId = await this.addNetscalerLoadBalancer(params)
if (jobId) {
await this.$store.dispatch('AddAsyncJob', {
title: this.$t(this.action.label),
jobid: jobId,
description: this.$t(this.nsp.name),
status: 'progress'
})
await this.parentPollActionCompletion(jobId, this.action)
}
this.loading = false
await this.provideCloseAction()
} catch (error) {
this.loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
})
},
addNetworkServiceProvider (args) {
return new Promise((resolve, reject) => {
api('addNetworkServiceProvider', args).then(async json => {
const jobId = json.addnetworkserviceproviderresponse.jobid
if (jobId) {
const result = await this.pollJob(jobId)
if (result.jobstatus === 2) {
reject(result.jobresult.errortext)
return
}
resolve(result.jobresult.networkserviceprovider)
}
}).catch(error => {
reject(error)
})
})
},
addNetscalerLoadBalancer (args) {
return new Promise((resolve, reject) => {
api('addNetscalerLoadBalancer', args).then(json => {
const jobId = json.addnetscalerloadbalancerresponse.jobid || null
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
async pollJob (jobId) {
return new Promise(resolve => {
const asyncJobInterval = setInterval(() => {
api('queryAsyncJobResult', { jobId }).then(async json => {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 0) {
return
}
clearInterval(asyncJobInterval)
resolve(result)
})
}, 1000)
})
}
}
}
</script>
<style scoped lang="less">
.form-layout {
.action-button {
text-align: right;
margin-top: 20px;
button {
margin-right: 5px;
}
}
}
</style>

View File

@ -0,0 +1,233 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="form-layout">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmit">
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.ip')">
<a-input
v-decorator="['ip', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.username')">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.password')">
<a-input-password
v-decorator="['password', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.numretries')">
<a-input-number
style="width: 100%"
v-decorator="['numretries', { initialValue: 2 }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.transportzoneuuid')">
<a-input
v-decorator="['transportzoneuuid']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.l3gatewayserviceuuid')">
<a-input
v-decorator="['l3gatewayserviceuuid']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.l2gatewayserviceuuid')">
<a-input
v-decorator="['l2gatewayserviceuuid']" />
</a-form-item>
</a-col>
</a-row>
<div :span="24" class="action-button">
<a-button :loading="loading" @click="onCloseAction">{{ this.$t('Cancel') }}</a-button>
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { api } from '@/api'
export default {
name: 'AddNiciraNvpDevice',
props: {
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
loading: false,
nsp: {}
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
mounted () {
if (this.resource && Object.keys(this.resource).length > 0) {
this.nsp = this.resource
}
},
inject: ['provideCloseAction', 'provideReload', 'provideCloseAction', 'parentPollActionCompletion'],
methods: {
onCloseAction () {
this.provideCloseAction()
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields(async (err, values) => {
if (err) {
return
}
const params = {}
params.physicalnetworkid = this.resource.physicalnetworkid
params.hostname = values.ip
params.username = values.username
params.password = values.password
params.transportzoneuuid = values.transportzoneuuid ? values.transportzoneuuid : null
params.l2gatewayserviceuuid = values.l2gatewayserviceuuid ? values.l2gatewayserviceuuid : null
params.l3gatewayserviceuuid = values.l3gatewayserviceuuid ? values.l3gatewayserviceuuid : null
this.loading = true
try {
if (!this.nsp.id) {
const addParams = {}
addParams.name = this.resource.name
addParams.physicalnetworkid = this.resource.physicalnetworkid
const networkServiceProvider = await this.addNetworkServiceProvider(addParams)
this.nsp = { ...this.resource, ...networkServiceProvider }
}
params.id = this.nsp.id
const jobId = await this.addNiciraNvpDevice(params)
if (jobId) {
await this.$store.dispatch('AddAsyncJob', {
title: this.$t(this.action.label),
jobid: jobId,
description: this.$t(this.nsp.name),
status: 'progress'
})
await this.parentPollActionCompletion(jobId, this.action)
}
this.loading = false
await this.provideCloseAction()
} catch (error) {
this.loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
})
},
addNetworkServiceProvider (args) {
return new Promise((resolve, reject) => {
api('addNetworkServiceProvider', args).then(async json => {
const jobId = json.addnetworkserviceproviderresponse.jobid
if (jobId) {
const result = await this.pollJob(jobId)
if (result.jobstatus === 2) {
reject(result.jobresult.errortext)
return
}
resolve(result.jobresult.networkserviceprovider)
}
}).catch(error => {
reject(error)
})
})
},
addNiciraNvpDevice (args) {
return new Promise((resolve, reject) => {
api('addNiciraNvpDevice', args).then(json => {
const jobId = json.addniciranvpdeviceresponse.jobid || null
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
async pollJob (jobId) {
return new Promise(resolve => {
const asyncJobInterval = setInterval(() => {
api('queryAsyncJobResult', { jobId }).then(async json => {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 0) {
return
}
clearInterval(asyncJobInterval)
resolve(result)
})
}, 1000)
})
}
}
}
</script>
<style scoped lang="less">
.form-layout {
.action-button {
text-align: right;
margin-top: 20px;
button {
margin-right: 5px;
}
}
}
</style>

View File

@ -0,0 +1,445 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="form-layout">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmit">
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.ip')">
<a-input
v-decorator="['ip', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.username')">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.password')">
<a-input-password
v-decorator="['password', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.networkdevicetype')">
<a-select
v-decorator="['networkdevicetype', {
rules: [{ required: true, message: $t('message.error.select') }]
}]">
<a-select-option
v-for="opt in networkDeviceType"
:key="opt.id">{{ $t(opt.description) }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.publicinterface')">
<a-input
v-decorator="['publicinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.privateinterface')">
<a-input
v-decorator="['privateinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.usageinterface')">
<a-input
v-decorator="['usageinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.numretries')">
<a-input-number
style="width: 100%"
v-decorator="['numretries', { initialValue: 2 }]" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.timeout')">
<a-input-number
style="width: 100%"
v-decorator="['timeout', { initialValue: 300 }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.publicnetwork')">
<a-input
v-decorator="['publicnetwork', { initialValue: 'untrusted' }]"
:disabled="true" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.privatenetwork')">
<a-input
v-decorator="['privatenetwork', { initialValue: 'trusted' }]"
:disabled="true" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.pavr')">
<a-input
v-decorator="['pavr']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.patp')">
<a-input
v-decorator="['patp']" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.palp')">
<a-input
v-decorator="['palp']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.capacity')">
<a-input
v-decorator="['capacity']" />
</a-form-item>
</a-col>
</a-row>
<div :span="24" class="action-button">
<a-button :loading="loading" @click="onCloseAction">{{ this.$t('Cancel') }}</a-button>
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { api } from '@/api'
export default {
name: 'AddPaloAltoFirewall',
props: {
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
loading: false,
nsp: {}
}
},
computed: {
networkDeviceType () {
const items = []
items.push({
id: 'PaloAltoFirewall',
description: 'label.palo.alto.firewall'
})
return items
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
mounted () {
if (this.resource && Object.keys(this.resource).length > 0) {
this.nsp = this.resource
}
},
inject: ['provideCloseAction', 'provideReload', 'provideCloseAction', 'parentPollActionCompletion'],
methods: {
onCloseAction () {
this.provideCloseAction()
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields(async (err, values) => {
if (err) {
return
}
const params = {}
params.physicalnetworkid = this.resource.physicalnetworkid
params.username = values.username
params.password = values.password
params.networkdevicetype = values.networkdevicetype
const url = []
const ip = values.ip
url.push('https://' + ip)
let isQuestionMarkAdded = false
const publicInterface = values.publicinterface ? values.publicinterface : null
if (publicInterface != null && publicInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicinterface=' + publicInterface)
}
const privateInterface = values.privateinterface ? values.privateinterface : null
if (privateInterface != null && privateInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privateinterface=' + publicInterface)
}
const usageInterface = values.usageinterface ? values.usageinterface : null
if (usageInterface != null && usageInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('usageinterface=' + usageInterface)
}
const numretries = values.numretries ? values.numretries : null
if (numretries != null && numretries.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('numretries=' + numretries)
}
const timeout = values.timeout ? values.timeout : null
if (timeout != null && timeout.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('timeout=' + timeout)
}
const publicNetwork = values.publicnetwork ? values.publicnetwork : null
if (publicNetwork != null && publicNetwork.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicnetwork=' + publicNetwork)
}
const privateNetwork = values.privatenetwork ? values.privatenetwork : null
if (privateNetwork != null && privateNetwork.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privatenetwork=' + privateNetwork)
}
const capacity = values.capacity ? values.capacity : null
if (capacity != null && capacity.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicecapacity=' + capacity)
}
const dedicated = values.dedicated ? values.dedicated : false
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicededicated=' + dedicated)
const externalVirtualRouter = values.pavr ? values.pavr : null
if (externalVirtualRouter != null && externalVirtualRouter.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('pavr=' + externalVirtualRouter)
}
const externalThreatProfile = values.patp ? values.patp : null
if (externalThreatProfile != null && externalThreatProfile.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('patp=' + externalThreatProfile)
}
const externalLogProfile = values.palp ? values.palp : null
if (externalLogProfile != null && externalLogProfile.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('palp=' + externalLogProfile)
}
params.url = url.join('')
this.loading = true
try {
if (!this.nsp.id) {
const addParams = {}
addParams.name = this.resource.name
addParams.physicalnetworkid = this.resource.physicalnetworkid
const networkServiceProvider = await this.addNetworkServiceProvider(addParams)
this.nsp = { ...this.resource, ...networkServiceProvider }
}
params.id = this.nsp.id
const jobId = await this.addPaloAltoFirewall(params)
if (jobId) {
await this.$store.dispatch('AddAsyncJob', {
title: this.$t(this.action.label),
jobid: jobId,
description: this.$t(this.nsp.name),
status: 'progress'
})
await this.parentPollActionCompletion(jobId, this.action)
}
this.loading = false
await this.provideCloseAction()
} catch (error) {
this.loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
})
},
addNetworkServiceProvider (args) {
return new Promise((resolve, reject) => {
api('addNetworkServiceProvider', args).then(async json => {
const jobId = json.addnetworkserviceproviderresponse.jobid
if (jobId) {
const result = await this.pollJob(jobId)
if (result.jobstatus === 2) {
reject(result.jobresult.errortext)
return
}
resolve(result.jobresult.networkserviceprovider)
}
}).catch(error => {
reject(error)
})
})
},
addPaloAltoFirewall (args) {
return new Promise((resolve, reject) => {
api('addPaloAltoFirewall', args).then(json => {
const jobId = json.addpaloaltofirewallresponse.jobid || null
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
async pollJob (jobId) {
return new Promise(resolve => {
const asyncJobInterval = setInterval(() => {
api('queryAsyncJobResult', { jobId }).then(async json => {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 0) {
return
}
clearInterval(asyncJobInterval)
resolve(result)
})
}, 1000)
})
}
}
}
</script>
<style scoped lang="less">
.form-layout {
.action-button {
text-align: right;
margin-top: 20px;
button {
margin-right: 5px;
}
}
}
</style>

View File

@ -0,0 +1,390 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
<template>
<div class="form-layout">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmit">
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.ip')">
<a-input
v-decorator="['ip', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.username')">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.password')">
<a-input-password
v-decorator="['password', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.networkdevicetype')">
<a-select
v-decorator="['networkdevicetype', {
rules: [{ required: true, message: $t('message.error.select') }]
}]">
<a-select-option
v-for="opt in networkDeviceType"
:key="opt.id">{{ $t(opt.description) }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.publicinterface')">
<a-input
v-decorator="['publicinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.privateinterface')">
<a-input
v-decorator="['privateinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.usageinterface')">
<a-input
v-decorator="['usageinterface']" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.numretries')">
<a-input-number
style="width: 100%"
v-decorator="['numretries', { initialValue: 2 }]" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.timeout')">
<a-input-number
style="width: 100%"
v-decorator="['timeout', { initialValue: 300 }]" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.publicnetwork')">
<a-input
v-decorator="['publicnetwork', { initialValue: 'untrusted' }]"
:disabled="true" />
</a-form-item>
</a-col>
<a-col :md="12" :lg="12">
<a-form-item :label="$t('label.privatenetwork')">
<a-input
v-decorator="['privatenetwork', { initialValue: 'trusted' }]"
:disabled="true" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="12">
<a-col :md="24" :lg="24">
<a-form-item :label="$t('label.capacity')">
<a-input
v-decorator="['capacity']" />
</a-form-item>
</a-col>
</a-row>
<div :span="24" class="action-button">
<a-button :loading="loading" @click="onCloseAction">{{ this.$t('Cancel') }}</a-button>
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
</div>
</a-form>
</div>
</template>
<script>
import { api } from '@/api'
export default {
name: 'AddSrxFirewall',
props: {
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
loading: false,
nsp: {}
}
},
computed: {
networkDeviceType () {
const items = []
items.push({
id: 'JuniperSRXFirewall',
description: 'label.srx.firewall'
})
return items
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
mounted () {
if (this.resource && Object.keys(this.resource).length > 0) {
this.nsp = this.resource
}
},
inject: ['provideCloseAction', 'provideReload', 'provideCloseAction', 'parentPollActionCompletion'],
methods: {
onCloseAction () {
this.provideCloseAction()
},
handleSubmit (e) {
e.preventDefault()
this.form.validateFields(async (err, values) => {
if (err) {
return
}
const params = {}
params.physicalnetworkid = this.resource.physicalnetworkid
params.username = values.username
params.password = values.password
params.networkdevicetype = values.networkdevicetype
const url = []
const ip = values.ip
url.push('https://' + ip)
let isQuestionMarkAdded = false
const publicInterface = values.publicinterface ? values.publicinterface : null
if (publicInterface != null && publicInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicinterface=' + publicInterface)
}
const privateInterface = values.privateinterface ? values.privateinterface : null
if (privateInterface != null && privateInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privateinterface=' + publicInterface)
}
const usageInterface = values.usageinterface ? values.usageinterface : null
if (usageInterface != null && usageInterface.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('usageinterface=' + usageInterface)
}
const numretries = values.numretries ? values.numretries : null
if (numretries != null && numretries.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('numretries=' + numretries)
}
const timeout = values.timeout ? values.timeout : null
if (timeout != null && timeout.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('timeout=' + timeout)
}
const publicNetwork = values.publicnetwork ? values.publicnetwork : null
if (publicNetwork != null && publicNetwork.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('publicnetwork=' + publicNetwork)
}
const privateNetwork = values.privatenetwork ? values.privatenetwork : null
if (privateNetwork != null && privateNetwork.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('privatenetwork=' + privateNetwork)
}
const capacity = values.capacity ? values.capacity : null
if (capacity != null && capacity.length > 0) {
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicecapacity=' + capacity)
}
const dedicated = values.dedicated ? values.dedicated : false
if (!isQuestionMarkAdded) {
url.push('?')
isQuestionMarkAdded = true
} else {
url.push('&')
}
url.push('lbdevicededicated=' + dedicated)
params.url = url.join('')
this.loading = true
try {
if (!this.nsp.id) {
const addParams = {}
addParams.name = this.resource.name
addParams.physicalnetworkid = this.resource.physicalnetworkid
const networkServiceProvider = await this.addNetworkServiceProvider(addParams)
this.nsp = { ...this.resource, ...networkServiceProvider }
}
params.id = this.nsp.id
const jobId = await this.addSrxFirewall(params)
if (jobId) {
await this.$store.dispatch('AddAsyncJob', {
title: this.$t(this.action.label),
jobid: jobId,
description: this.$t(this.nsp.name),
status: 'progress'
})
await this.parentPollActionCompletion(jobId, this.action)
}
this.loading = false
await this.provideCloseAction()
} catch (error) {
this.loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
})
},
addNetworkServiceProvider (args) {
return new Promise((resolve, reject) => {
api('addNetworkServiceProvider', args).then(async json => {
const jobId = json.addnetworkserviceproviderresponse.jobid
if (jobId) {
const result = await this.pollJob(jobId)
if (result.jobstatus === 2) {
reject(result.jobresult.errortext)
return
}
resolve(result.jobresult.networkserviceprovider)
}
}).catch(error => {
reject(error)
})
})
},
addSrxFirewall (args) {
return new Promise((resolve, reject) => {
api('addSrxFirewall', args).then(json => {
const jobId = json.addsrxfirewallresponse.jobid || null
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
async pollJob (jobId) {
return new Promise(resolve => {
const asyncJobInterval = setInterval(() => {
api('queryAsyncJobResult', { jobId }).then(async json => {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 0) {
return
}
clearInterval(asyncJobInterval)
resolve(result)
})
}, 1000)
})
}
}
}
</script>
<style scoped lang="less">
.form-layout {
.action-button {
text-align: right;
margin-top: 20px;
button {
margin-right: 5px;
}
}
}
</style>

View File

@ -0,0 +1,60 @@
// 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>
<a-list size="small" :dataSource="details">
<a-list-item slot="renderItem" slot-scope="item">
<div>
<strong>{{ $t(`label.${item}`) }}</strong>
<br />
<div v-if="item === 'servicelist'">
{{ nsp[item] ? nsp[item].join(', ') : '' }}
</div>
<span v-else-if="item !== 'state'">{{ nsp[item] ? nsp[item] : '' }}</span>
<span v-else>
<status :text="nsp[item] ? nsp[item] : 'Disabled'" displayText />
</span>
</div>
</a-list-item>
</a-list>
</div>
</template>
<script>
import Status from '@/components/widgets/Status'
export default {
name: 'ProviderDetail',
components: {
Status
},
props: {
details: {
type: Array,
default: () => []
},
nsp: {
type: Object,
default: () => {}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,256 @@
// 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>
<a-row :gutter="12">
<a-col :md="24" :lg="24" style="text-align: left">
<action-button
:actions="provider.actions"
:resource="resource"
:loading="loading"
@exec-action="handleExecAction"/>
</a-col>
</a-row>
<provider-detail
style="margin-top: 10px"
:details="provider.details"
:nsp="resource"
:loading="loading" />
<div
v-for="(list, index) in listData"
:key="index">
<provider-list-view
style="border-top: 1px solid #ddd; padding-top: 5px;"
v-if="resource.id"
:title="list.title"
:action="currentAction"
:dataSource="list.data"
:columns="list.columns"
:itemCount="list.itemCount"
:resource="resource"
:page="page"
:pageSize="pageSize"
:loading="loading || list.loading" />
</div>
</div>
</template>
<script>
import { api } from '@/api'
import ActionButton from '@/components/view/ActionButton'
import ProviderDetail from '@/views/infra/network/providers/ProviderDetail'
import ProviderListView from '@/views/infra/network/providers/ProviderListView'
export default {
name: 'ProviderItem',
components: {
ActionButton,
ProviderDetail,
ProviderListView
},
props: {
itemNsp: {
type: Object,
required: true
},
nsp: {
type: Object,
default: () => {}
},
resourceId: {
type: String,
default: () => ''
},
zoneId: {
type: String,
default: () => ''
},
loading: {
type: Boolean,
default: false
}
},
data () {
return {
provider: {},
listData: {},
resource: {},
currentAction: {},
page: 1,
pageSize: 10,
itemCount: 0
}
},
provide () {
return {
providerChangePage: this.changePage
}
},
inject: ['provideSetNsp', 'provideExecuteAction'],
watch: {
loading (newValue, oldValue) {
if (newValue !== oldValue && !newValue) {
this.fetchData()
}
},
nsp (newData, oldData) {
if (newData && Object.keys(newData).length > 0) {
this.nsp = newData
this.resource = this.nsp
this.$set(this.resource, 'zoneid', this.zoneId)
this.provideSetNsp(this.resource)
this.fetchData()
}
}
},
mounted () {
if (!this.nsp || Object.keys(this.nsp).length === 0) {
this.resource = {
name: this.itemNsp.title,
state: 'Disabled',
physicalnetworkid: this.resourceId,
zoneid: this.zoneId
}
} else {
this.resource = this.nsp
this.$set(this.resource, 'zoneid', this.zoneId)
}
if (this.itemNsp && Object.keys(this.itemNsp).length > 0) {
this.provider = this.itemNsp
this.provideSetNsp(this.resource)
this.fetchData()
}
},
methods: {
async fetchData () {
if (!this.provider.lists || this.provider.lists.length === 0) {
return
}
this.provider.lists.map(this.fetchOptions)
},
async fetchOptions (args) {
if (!args || Object.keys(args).length === 0) {
return
}
const params = {}
if (args.mapping) {
Object.keys(args.mapping).map(key => {
params[key] = 'value' in args.mapping[key] ? args.mapping[key].value(this.resource) : null
})
}
params.page = this.page
params.pageSize = this.pageSize
let length = args.columns.length
if (args.columns.includes('action')) {
length--
}
const columns = args.columns.map(col => {
if (col === 'action') {
return {
title: this.$t('label.' + col),
dataIndex: col,
width: 80,
fixed: 'right',
scopedSlots: { customRender: col }
}
}
const width = 100 / (length) + '%'
return {
title: this.$t('label.' + col),
width: width,
dataIndex: col,
scopedSlots: { customRender: col }
}
})
this.listData[args.title] = {
title: args.title,
columns: columns,
data: [],
itemCount: 0,
loading: true
}
try {
const listResult = await this.executeApi(args.api, params)
this.listData[args.title].data = listResult.data
this.listData[args.title].itemCount = listResult.itemCount
this.listData[args.title].loading = false
} catch (error) {
this.listData[args.title].loading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
this.$forceUpdate()
},
executeApi (apiName, params) {
return new Promise((resolve, reject) => {
api(apiName, params).then(json => {
let responseName
let objectName
let itemCount = 0
const result = {
data: [],
itemCount: 0
}
for (const key in json) {
if (key.includes('response') || key.includes(apiName)) {
responseName = key
break
}
}
if (!responseName) {
resolve(result)
return
}
for (const key in json[responseName]) {
if (key === 'count') {
itemCount = json[responseName].count
continue
}
objectName = key
break
}
result.data = json[responseName][objectName] || []
result.itemCount = itemCount
resolve(result)
}).catch(e => {
reject(e)
})
})
},
handleExecAction (action) {
this.currentAction = action
this.provideExecuteAction(action)
},
changePage (listName, page, pageSize) {
this.page = page
this.pageSize = pageSize
const listItem = this.provider.lists.filter(provider => provider.title === listName)[0] || {}
this.fetchOptions(listItem)
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,385 @@
// 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>
<strong>{{ $t(title) }}</strong>
<a-table
style="margin-top: 10px;"
size="small"
class="row-list-data"
:loading="loading"
:columns="listCols"
:dataSource="dataSource"
:rowKey="record => record.id || record.name || record.nvpdeviceid || record.resourceid"
:pagination="false"
:scroll="scrollable">
<template slot="name" slot-scope="text, record">
<span v-if="record.role==='VIRTUAL_ROUTER'">
<router-link :to="{ path: '/router' + '/' + record.id }" v-if="record.id">{{ text }}</router-link>
<label v-else>{{ text }}</label>
</span>
<span v-else>{{ text }}</span>
</template>
<template slot="hostname" slot-scope="text, record">
<span v-if="record.role==='VIRTUAL_ROUTER'">
<router-link :to="{ path: '/host' + '/' + record.hostid }" v-if="record.hostid">{{ text }}</router-link>
<label v-else>{{ text }}</label>
</span>
<span v-else>{{ text }}</span>
</template>
<template slot="zonename" slot-scope="text, record">
<span v-if="record.role==='VIRTUAL_ROUTER'">
<router-link :to="{ path: '/zone' + '/' + record.zoneid }" v-if="record.zoneid">{{ text }}</router-link>
<label v-else>{{ text }}</label>
</span>
<span v-else>{{ text }}</span>
</template>
<template slot="action" slot-scope="text, record">
<a-tooltip placement="top">
<template slot="title">
<span v-if="resource.name==='BigSwitchBcf'">{{ $t('label.delete.bigswitchbcf') }}</span>
<span v-else-if="resource.name==='BrocadeVcs'">{{ $t('label.delete.brocadevcs') }}</span>
<span v-else-if="resource.name==='NiciraNvp'">{{ $t('label.delete.niciranvp') }}</span>
<span v-else-if="resource.name==='F5BigIp'">{{ $t('label.delete.f5') }}</span>
<span v-else-if="resource.name==='JuniperSRX'">{{ $t('label.delete.srx') }}</span>
<span v-else-if="resource.name==='Netscaler'">{{ $t('label.delete.netscaler') }}</span>
<span v-else-if="resource.name==='Opendaylight'">{{ $t('label.delete.opendaylight.device') }}</span>
<span v-else-if="resource.name==='PaloAlto'">{{ $t('label.delete.pa') }}</span>
<span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoVnmcResources'">
{{ $t('label.delete.ciscovnmc.resource') }}
</span>
<span v-else-if="resource.name==='CiscoVnmc' && title==='listCiscoAsa1000vResources'">
{{ $t('label.delete.ciscoasa1000v') }}
</span>
</template>
<a-button
v-if="resource.name==='Ovs'"
type="default"
shape="circle"
icon="setting"
size="small"
:loading="actionLoading"
@click="onConfigureOvs(record)"/>
<a-button
v-else
type="danger"
shape="circle"
icon="close"
size="small"
:loading="actionLoading"
@click="onDelete(record)"/>
</a-tooltip>
</template>
<template slot="lbdevicestate" slot-scope="text">
<status :text="text ? text : ''" displayText />
</template>
<template slot="status" slot-scope="text">
<status :text="text ? text : ''" displayText />
</template>
<template slot="state" slot-scope="text">
<status :text="text ? text : ''" displayText />
</template>
</a-table>
<a-pagination
size="small"
class="row-pagination"
:current="page"
:pageSize="pageSize"
:total="itemCount"
:showTotal="total => `Total ${total} items`"
:pageSizeOptions="['10', '20', '40', '80', '100']"
@change="changePage"
@showSizeChange="changePageSize"
showSizeChanger
showQuickJumper />
</div>
</template>
<script>
import { api } from '@/api'
import Status from '@/components/widgets/Status'
export default {
name: 'ProviderListView',
components: { Status },
props: {
title: {
type: String,
required: true
},
columns: {
type: Array,
required: true
},
dataSource: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
},
page: {
type: Number,
default: () => 1
},
pageSize: {
type: Number,
default: () => 10
},
itemCount: {
type: Number,
default: () => 0
},
resource: {
type: Object,
default: () => {}
},
action: {
type: Object,
default: () => {}
}
},
data () {
return {
actionLoading: false
}
},
computed: {
scrollable () {
if (this.dataSource.length === 0) {
return { y: '60vh', x: 'auto' }
} else if (this.columns.length > 3) {
return { y: '60vh', x: '50vw' }
}
return { y: '60vh' }
},
listCols () {
const columns = []
this.columns.forEach(col => {
if (col.dataIndex === 'hostname' && this.resource.name === 'BigSwitchBcf') {
col.title = this.$t('label.bigswitch.controller.address')
}
if (col.dataIndex === 'hostname' && this.resource.name === 'BrocadeVcs') {
col.title = this.$t('label.brocade.vcs.address')
}
columns.push(col)
})
return columns
}
},
inject: ['providerChangePage', 'provideReload', 'parentPollActionCompletion'],
methods: {
changePage (page, pageSize) {
this.providerChangePage(this.title, page, pageSize)
},
changePageSize (currentPage, pageSize) {
this.providerChangePage(this.title, currentPage, pageSize)
},
onDelete (record) {
let apiName
let confirmation
let label
let name
const params = {}
switch (this.resource.name) {
case 'BigSwitchBcf':
label = 'label.delete.bigswitchbcf'
name = record.hostname
apiName = 'deleteBigSwitchBcfDevice'
confirmation = 'message.confirm.delete.bigswitchbcf'
params.bcfdeviceid = record.bcfdeviceid
break
case 'F5BigIp':
label = 'label.delete.f5'
name = record.ipaddress
apiName = 'deleteF5LoadBalancer'
confirmation = 'message.confirm.delete.f5'
params.lbdeviceid = record.lbdeviceid
break
case 'NiciraNvp':
label = 'label.delete.niciranvp'
name = record.hostname
apiName = 'deleteNiciraNvpDevice'
confirmation = 'message.confirm.delete.niciranvp'
params.nvpdeviceid = record.nvpdeviceid
break
case 'BrocadeVcs':
label = 'label.delete.brocadevcs'
name = record.hostname
apiName = 'deleteBrocadeVcsDevice'
confirmation = 'message.confirm.delete.brocadevcs'
params.vcsdeviceid = record.vcsdeviceid
break
case 'JuniperSRX':
label = 'label.delete.srx'
name = record.ipaddress
apiName = 'deleteSrxFirewall'
confirmation = 'message.confirm.delete.srx'
params.fwdeviceid = record.fwdeviceid
break
case 'Netscaler':
label = 'label.delete.netscaler'
name = record.ipaddress
apiName = 'deleteNetscalerLoadBalancer'
confirmation = 'message.confirm.delete.netscaler'
params.lbdeviceid = record.lbdeviceid
break
case 'Opendaylight':
label = 'label.delete.opendaylight.device'
name = record.name
apiName = 'deleteOpenDaylightController'
confirmation = 'message.confirm.delete.Opendaylight'
params.id = record.id
break
case 'PaloAlto':
label = 'label.delete.PA'
name = record.ipaddress
apiName = 'deletePaloAltoFirewall'
confirmation = 'message.confirm.delete.pa'
params.fwdeviceid = record.fwdeviceid
break
case 'CiscoVnmc':
if (this.title === 'listCiscoVnmcResources') {
label = 'label.delete.ciscovnmc.resource'
apiName = 'deleteCiscoVnmcResource'
confirmation = 'message.confirm.delete.ciscovnmc.resource'
} else {
label = 'label.delete.ciscoasa1000v'
apiName = 'deleteCiscoAsa1000vResource'
confirmation = 'message.confirm.delete.ciscoasa1000v'
}
name = record.hostname
params.resourceid = record.resourceid
break
default:
break
}
this.$confirm({
title: this.$t('label.confirmation'),
content: this.$t(confirmation),
onOk: async () => {
if (apiName) {
this.actionLoading = true
try {
const jobId = await this.executeDeleteRecord(apiName, params)
if (jobId) {
this.$store.dispatch('AddAsyncJob', {
title: this.$t(label),
jobid: jobId,
description: this.$t(name),
status: 'progress'
})
this.parentPollActionCompletion(jobId, this.action)
} else {
this.$success('Success')
this.provideReload()
}
this.actionLoading = false
} catch (error) {
this.actionLoading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
}
}
})
},
onConfigureOvs (record) {
const params = {}
params.id = record.id
params.enabled = true
this.$confirm({
title: this.$t('label.confirmation'),
content: this.$t('message.confirm.configure.ovs'),
onOk: async () => {
this.actionLoading = true
try {
const jobId = await this.configureOvsElement(params)
if (jobId) {
this.$store.dispatch('AddAsyncJob', {
title: this.$t('label.configure.ovs'),
jobid: jobId,
description: this.$t(record.id),
status: 'progress'
})
this.parentPollActionCompletion(jobId, this.action)
} else {
this.$success('Success')
this.provideReload()
}
this.actionLoading = false
} catch (error) {
this.actionLoading = false
this.$notification.error({
message: 'Request Failed',
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
}
}
})
},
executeDeleteRecord (apiName, args) {
return new Promise((resolve, reject) => {
let jobId = null
api(apiName, args).then(json => {
for (const obj in json) {
if (obj.includes('response')) {
for (const res in json[obj]) {
if (res === 'jobid') {
jobId = json[obj][res]
break
}
}
break
}
}
resolve(jobId)
}).catch(error => {
reject(error)
})
})
},
configureOvsElement (args) {
return new Promise((resolve, reject) => {
api('configureOvsElement', args).then(json => {
const jobId = json.configureovselementresponse.jobid
resolve(jobId)
}).catch(error => {
reject(error)
})
})
}
}
}
</script>
<style scoped lang="less">
.row-pagination {
margin-top: 10px;
margin-bottom: 10px;
text-align: right;
}
</style>

View File

@ -564,14 +564,14 @@ export default {
for (let index = 0; index < this.stepData.physicalNetworksReturned.length; index++) {
const physicalNetwork = this.stepData.physicalNetworksReturned[index]
if (!this.stepData.stepMove.includes('advUpdatePhysicalNetwork' + index)) {
if (!this.stepData.stepMove.includes('advUpdatePhysicalNetwork' + physicalNetwork.id)) {
const updPhysicalParams = {}
updPhysicalParams.state = 'Enabled'
updPhysicalParams.id = physicalNetwork.id
try {
await this.updatePhysicalNetwork(updPhysicalParams)
this.stepData.stepMove.push('advUpdatePhysicalNetwork' + index)
this.stepData.stepMove.push('advUpdatePhysicalNetwork' + physicalNetwork.id)
} catch (e) {
this.messageError = e
this.processStatus = STATUS_FAILED
@ -581,7 +581,7 @@ export default {
}
// ***** Virtual Router ***** (begin) *****
if (!this.stepData.stepMove.includes('advVirtualRouter' + index)) {
if (!this.stepData.stepMove.includes('advVirtualRouter' + physicalNetwork.id)) {
const listParams = {}
listParams.name = 'VirtualRouter'
listParams.physicalNetworkId = physicalNetwork.id
@ -591,7 +591,7 @@ export default {
const elementId = await this.listVirtualRouterElements(providerId)
await this.configureVirtualRouterElement(elementId)
await this.updateNetworkServiceProvider(providerId)
this.stepData.stepMove.push('advVirtualRouter' + index)
this.stepData.stepMove.push('advVirtualRouter' + physicalNetwork.id)
} catch (e) {
this.messageError = e
this.processStatus = STATUS_FAILED
@ -639,7 +639,7 @@ export default {
}
},
async configOvs (physicalNetwork) {
if (this.stepData.stepMove.includes('configOvs')) {
if (this.stepData.stepMove.includes('configOvs' + physicalNetwork.id)) {
return
}
@ -656,10 +656,10 @@ export default {
}
}
this.stepData.stepMove.push('configOvs')
this.stepData.stepMove.push('configOvs' + physicalNetwork.id)
},
async configInternalLBVM (physicalNetwork) {
if (this.stepData.stepMove.includes('configInternalLBVM')) {
if (this.stepData.stepMove.includes('configInternalLBVM' + physicalNetwork.id)) {
return
}
@ -676,7 +676,7 @@ export default {
}
}
this.stepData.stepMove.push('configInternalLBVM')
this.stepData.stepMove.push('configInternalLBVM' + physicalNetwork.id)
},
async configVpcVirtualRouter (physicalNetwork) {
const listParams = {}
@ -684,13 +684,13 @@ export default {
listParams.physicalNetworkId = physicalNetwork.id
try {
if (!this.stepData.stepMove.includes('configVpcVirtualRouter')) {
if (!this.stepData.stepMove.includes('configVpcVirtualRouter' + physicalNetwork.id)) {
const providerId = await this.listNetworkServiceProviders(listParams)
const elementId = await this.listVirtualRouterElements(providerId)
await this.configureVirtualRouterElement(elementId)
await this.updateNetworkServiceProvider(providerId)
this.stepData.stepMove.push('configVpcVirtualRouter')
this.stepData.stepMove.push('configVpcVirtualRouter' + physicalNetwork.id)
}
} catch (e) {
this.messageError = e
@ -1496,10 +1496,10 @@ export default {
listNetworkParams.physicalNetworkId = this.stepData.physicalNetworkReturned.id
try {
if (!this.stepData.stepMove.includes('enableSecurityGroupProvider')) {
if (!this.stepData.stepMove.includes('enableSecurityGroupProvider' + this.stepData.physicalNetworkReturned.id)) {
const securityGroupProviderId = await this.listNetworkServiceProviders(listNetworkParams)
await this.updateNetworkServiceProvider(securityGroupProviderId, 'enableSecurityGroupProvider')
this.stepData.stepMove.push('enableSecurityGroupProvider')
this.stepData.stepMove.push('enableSecurityGroupProvider' + this.stepData.physicalNetworkReturned.id)
}
await this.stepAddNetscalerProvider()