Merge pull request #744 from utchoang/feature/fix-public-vpc-network
diff --git a/src/components/menu/SideMenu.vue b/src/components/menu/SideMenu.vue
index 20c76da..c670c9f 100644
--- a/src/components/menu/SideMenu.vue
+++ b/src/components/menu/SideMenu.vue
@@ -110,7 +110,6 @@
}
&.light {
- background-color: #fff;
box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05);
.ant-menu-light {
diff --git a/src/components/view/TreeView.vue b/src/components/view/TreeView.vue
index 9621754..0be5228 100644
--- a/src/components/view/TreeView.vue
+++ b/src/components/view/TreeView.vue
@@ -345,6 +345,7 @@
reloadTreeData (objData) {
if (objData && objData[0].isDel) {
this.treeVerticalData = this.treeVerticalData.filter(item => item.id !== objData[0].id)
+ this.treeVerticalData = this.treeVerticalData.filter(item => item.parentdomainid !== objData[0].id)
} else {
// data response from action
let jsonResponse = this.getResponseJsonData(objData[0])
diff --git a/src/config/section/domain.js b/src/config/section/domain.js
index a507ab6..7fbd263 100644
--- a/src/config/section/domain.js
+++ b/src/config/section/domain.js
@@ -130,7 +130,6 @@
listView: true,
dataView: true,
show: (record, store) => {
- console.log(record)
return ['Admin'].includes(store.userInfo.roletype) && record.level !== 0 ||
['DomainAdmin'].includes(store.userInfo.roletype) && record.domainid !== store.userInfo.domainid
},
diff --git a/src/locales/en.json b/src/locales/en.json
index b7af2d0..fa54ea1 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -25,6 +25,7 @@
"error.release.dedicate.zone": "Failed to release dedicated zone",
"error.session.expired": "Your session has expired.",
"error.unable.to.reach.management.server": "Unable to reach Management Server",
+"error.unable.to.proceed": "Unable to proceed. Please contact your administrator",
"error.unresolved.internet.name": "Your internet name cannot be resolved.",
"firewall.close": "Firewall",
"force.delete.domain.warning": "Warning: Choosing this option will cause the deletion of all child domains and all associated accounts and their resources.",
@@ -1465,6 +1466,7 @@
"label.no.grouping": "(no grouping)",
"label.no.isos": "No available ISOs",
"label.no.items": "No Available Items",
+"label.no.matching.offering": "No matching offering found",
"label.no.security.groups": "No Available Security Groups",
"label.noderootdisksize": "Node root disk size (in GB)",
"label.nodiskcache": "No disk cache",
diff --git a/src/permission.js b/src/permission.js
index 161a1b4..d22c00f 100644
--- a/src/permission.js
+++ b/src/permission.js
@@ -66,7 +66,8 @@
.catch(() => {
notification.error({
message: 'Error',
- description: i18n.t('message.error.discovering.feature')
+ description: i18n.t('message.error.discovering.feature'),
+ duration: 0
})
store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } })
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index 76271ff..aba087d 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -19,6 +19,7 @@
import Vue from 'vue'
import md5 from 'md5'
import message from 'ant-design-vue/es/message'
+import notification from 'ant-design-vue/es/notification'
import router from '@/router'
import store from '@/store'
import { login, logout, api } from '@/api'
@@ -112,6 +113,8 @@
commit('SET_LDAP', {})
commit('SET_CLOUDIAN', {})
+ notification.destroy()
+
resolve()
}).catch(error => {
reject(error)
@@ -148,6 +151,8 @@
api('listZones', { listall: true }).then(json => {
const zones = json.listzonesresponse.zone || []
commit('SET_ZONES', zones)
+ }).catch(error => {
+ reject(error)
})
api('listApis').then(response => {
const apis = {}
diff --git a/src/style/vars.less b/src/style/vars.less
index 399afd6..b422e53 100644
--- a/src/style/vars.less
+++ b/src/style/vars.less
@@ -89,6 +89,10 @@
box-shadow: 1px 1px 0px 0px #e8e8e8;
}
+.sider.light {
+ background: @navigation-background-color;
+}
+
.ant-menu {
background: @navigation-background-color;
}
diff --git a/src/utils/request.js b/src/utils/request.js
index ffc62eb..9371aee 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -37,16 +37,29 @@
notification.error({ message: i18n.t('label.forbidden'), description: data.message })
}
if (response.status === 401) {
- if (response.config && response.config.params && ['listIdps'].includes(response.config.params.command)) {
+ if (response.config && response.config.params && ['listIdps', 'cloudianIsEnabled'].includes(response.config.params.command)) {
return
}
+ for (const key in response.data) {
+ if (key.includes('response')) {
+ if (response.data[key].errortext.includes('not available for user')) {
+ notification.error({
+ message: 'Error',
+ description: response.data[key].errortext + ' ' + i18n.t('error.unable.to.proceed'),
+ duration: 0
+ })
+ return
+ }
+ }
+ }
notification.error({
message: i18n.t('label.unauthorized'),
description: i18n.t('message.authorization.failed'),
- key: 'http-401'
+ key: 'http-401',
+ duration: 0
})
store.dispatch('Logout').then(() => {
- router.go(0)
+ router.push({ path: '/user/login', query: { redirect: router.history.current.fullPath } })
})
}
if (response.status === 404) {
diff --git a/src/views/AutogenView.vue b/src/views/AutogenView.vue
index f908fb1..f607ea5 100644
--- a/src/views/AutogenView.vue
+++ b/src/views/AutogenView.vue
@@ -651,6 +651,10 @@
}
}
}).catch(error => {
+ if ([401].includes(error.response.status)) {
+ return
+ }
+
if (Object.keys(this.searchParams).length > 0) {
this.itemCount = 0
this.items = []
@@ -661,13 +665,6 @@
return
}
- if ([401].includes(error.response.status)) {
- store.dispatch('Logout').then(() => {
- this.$router.push({ path: '/user/login', query: { redirect: this.$route.fullPath } })
- })
- return
- }
-
this.$notifyError(error)
if ([405].includes(error.response.status)) {
@@ -895,6 +892,9 @@
api(action.api, params).then(json => {
this.handleResponse(json, resourceName, action, false)
}).catch(error => {
+ if ([401].includes(error.response.status)) {
+ return
+ }
this.$notifyError(error)
})
},
@@ -995,6 +995,10 @@
}
this.closeAction()
}).catch(error => {
+ if ([401].includes(error.response.status)) {
+ return
+ }
+
console.log(error)
this.$notifyError(error)
}).finally(f => {
diff --git a/src/views/compute/DeployVM.vue b/src/views/compute/DeployVM.vue
index a4b42bf..6ad55aa 100644
--- a/src/views/compute/DeployVM.vue
+++ b/src/views/compute/DeployVM.vue
@@ -226,6 +226,21 @@
</template>
</a-step>
<a-step
+ :title="$t('label.data.disk')"
+ :status="zoneSelected ? 'process' : 'wait'"
+ v-if="!template.deployasis && template.childtemplates && template.childtemplates.length > 0" >
+ <template slot="description">
+ <div v-if="zoneSelected">
+ <multi-disk-selection
+ :items="template.childtemplates"
+ :diskOfferings="options.diskOfferings"
+ :zoneId="zoneId"
+ @select-multi-disk-offering="updateMultiDiskOffering($event)" />
+ </div>
+ </template>
+ </a-step>
+ <a-step
+ v-else
:title="tabKey == 'templateid' ? $t('label.data.disk') : $t('label.disk.size')"
:status="zoneSelected ? 'process' : 'wait'">
<template slot="description">
@@ -607,6 +622,7 @@
import ComputeSelection from '@views/compute/wizard/ComputeSelection'
import DiskOfferingSelection from '@views/compute/wizard/DiskOfferingSelection'
import DiskSizeSelection from '@views/compute/wizard/DiskSizeSelection'
+import MultiDiskSelection from '@views/compute/wizard/MultiDiskSelection'
import TemplateIsoSelection from '@views/compute/wizard/TemplateIsoSelection'
import AffinityGroupSelection from '@views/compute/wizard/AffinityGroupSelection'
import NetworkSelection from '@views/compute/wizard/NetworkSelection'
@@ -623,6 +639,7 @@
AffinityGroupSelection,
TemplateIsoSelection,
DiskSizeSelection,
+ MultiDiskSelection,
DiskOfferingSelection,
InfoCard,
ComputeOfferingSelection,
@@ -1040,7 +1057,11 @@
}
}
- if (this.diskOffering) {
+ if (!this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
+ this.vm.diskofferingid = ''
+ this.vm.diskofferingname = ''
+ this.vm.diskofferingsize = ''
+ } else if (this.diskOffering) {
this.vm.diskofferingid = this.diskOffering.id
this.vm.diskofferingname = this.diskOffering.displaytext
this.vm.diskofferingsize = this.diskOffering.disksize
@@ -1072,6 +1093,7 @@
})
this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('diskofferingid', { initialValue: undefined, preserve: true })
+ this.form.getFieldDecorator('multidiskoffering', { initialValue: undefined, preserve: true })
this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true })
this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true })
this.form.getFieldDecorator('keypair', { initialValue: undefined, preserve: true })
@@ -1286,6 +1308,11 @@
diskofferingid: id
})
},
+ updateMultiDiskOffering (value) {
+ this.form.setFieldsValue({
+ multidiskoffering: value
+ })
+ },
updateAffinityGroups (ids) {
this.form.setFieldsValue({
affinitygroupids: ids
@@ -1408,9 +1435,22 @@
deployVmData['details[0].configurationId'] = this.selectedTemplateConfiguration.id
}
// step 4: select disk offering
- deployVmData.diskofferingid = values.diskofferingid
- if (values.size) {
- deployVmData.size = values.size
+ if (!this.template.deployasis && this.template.childtemplates && this.template.childtemplates.length > 0) {
+ if (values.multidiskoffering) {
+ let i = 0
+ Object.entries(values.multidiskoffering).forEach(([disk, offering]) => {
+ const diskKey = `datadiskofferinglist[${i}].datadisktemplateid`
+ const offeringKey = `datadiskofferinglist[${i}].diskofferingid`
+ deployVmData[diskKey] = disk
+ deployVmData[offeringKey] = offering
+ i++
+ })
+ }
+ } else {
+ deployVmData.diskofferingid = values.diskofferingid
+ if (values.size) {
+ deployVmData.size = values.size
+ }
}
// step 5: select an affinity group
deployVmData.affinitygroupids = (values.affinitygroupids || []).join(',')
diff --git a/src/views/compute/wizard/DiskOfferingSelection.vue b/src/views/compute/wizard/DiskOfferingSelection.vue
index 4578363..aff96a2 100644
--- a/src/views/compute/wizard/DiskOfferingSelection.vue
+++ b/src/views/compute/wizard/DiskOfferingSelection.vue
@@ -133,6 +133,9 @@
},
created () {
this.initDataItem()
+ if (this.items) {
+ this.dataItems = this.dataItems.concat(this.items)
+ }
},
computed: {
tableSource () {
diff --git a/src/views/compute/wizard/MultiDiskSelection.vue b/src/views/compute/wizard/MultiDiskSelection.vue
new file mode 100644
index 0000000..998fdbe
--- /dev/null
+++ b/src/views/compute/wizard/MultiDiskSelection.vue
@@ -0,0 +1,170 @@
+// 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-table
+ :loading="loading"
+ :columns="columns"
+ :dataSource="tableSource"
+ :rowKey="record => record.id"
+ :pagination="false"
+ :rowSelection="rowSelection"
+ :scroll="{ y: 225 }" >
+
+ <span slot="offering" slot-scope="text, record">
+ <a-select
+ v-if="validOfferings[record.id] && validOfferings[record.id].length > 0"
+ @change="updateOffering($event, record.id)"
+ :defaultValue="validOfferings[record.id][0].id">
+ <a-select-option v-for="offering in validOfferings[record.id]" :key="offering.id">
+ {{ offering.displaytext }}
+ </a-select-option>
+ </a-select>
+ <span v-else>
+ {{ $t('label.no.matching.offering') }}
+ </span>
+ </span>
+ </a-table>
+ </div>
+</template>
+
+<script>
+import { api } from '@/api'
+
+export default {
+ name: 'MultiDiskSelection',
+ props: {
+ items: {
+ type: Array,
+ default: () => []
+ },
+ zoneId: {
+ type: String,
+ default: () => ''
+ }
+ },
+ data () {
+ return {
+ columns: [
+ {
+ dataIndex: 'name',
+ title: this.$t('label.data.disk')
+ },
+ {
+ dataIndex: 'offering',
+ title: this.$t('label.data.disk.offering'),
+ scopedSlots: { customRender: 'offering' }
+ }
+ ],
+ loading: false,
+ selectedRowKeys: [],
+ diskOfferings: [],
+ validOfferings: {},
+ values: {}
+ }
+ },
+ computed: {
+ tableSource () {
+ return this.items.map(item => {
+ return {
+ id: item.id,
+ name: `${item.name} (${item.size} GB)`,
+ disabled: this.validOfferings[item.id] && this.validOfferings[item.id].length === 0
+ }
+ })
+ },
+ rowSelection () {
+ return {
+ type: 'checkbox',
+ selectedRowKeys: this.selectedRowKeys,
+ getCheckboxProps: record => ({
+ props: {
+ disabled: record.disabled
+ }
+ }),
+ onChange: (rows) => {
+ this.selectedRowKeys = rows
+ this.sendValues()
+ }
+ }
+ }
+ },
+ watch: {
+ items (newData, oldData) {
+ this.items = newData
+ this.selectedRowKeys = []
+ this.fetchDiskOfferings()
+ },
+ zoneId (newData) {
+ this.zoneId = newData
+ this.fetchDiskOfferings()
+ }
+ },
+ created () {
+ this.fetchDiskOfferings()
+ },
+ methods: {
+ fetchDiskOfferings () {
+ this.diskOfferings = []
+ this.loading = true
+ api('listDiskOfferings', {
+ zoneid: this.zoneId,
+ listall: true
+ }).then(response => {
+ this.diskOfferings = response.listdiskofferingsresponse.diskoffering || []
+ this.diskOfferings = this.diskOfferings.filter(x => !x.iscustomized)
+ this.orderDiskOfferings()
+ }).finally(() => {
+ this.loading = false
+ })
+ },
+ orderDiskOfferings () {
+ this.loading = true
+ this.validOfferings = {}
+ for (const item of this.items) {
+ this.validOfferings[item.id] = this.diskOfferings.filter(x => x.disksize >= item.size)
+ }
+ this.setDefaultValues()
+ this.loading = false
+ },
+ setDefaultValues () {
+ this.values = {}
+ for (const item of this.items) {
+ this.values[item.id] = this.validOfferings[item.id].length > 0 ? this.validOfferings[item.id][0].id : ''
+ }
+ },
+ updateOffering (value, templateid) {
+ this.values[templateid] = value
+ this.sendValues()
+ },
+ sendValues () {
+ const data = {}
+ this.selectedRowKeys.map(x => {
+ data[x] = this.values[x]
+ })
+ this.$emit('select-multi-disk-offering', data)
+ }
+ }
+}
+</script>
+
+<style lang="less" scoped>
+ .ant-table-wrapper {
+ margin: 2rem 0;
+ }
+</style>
diff --git a/src/views/iam/DomainActionForm.vue b/src/views/iam/DomainActionForm.vue
index 6cfecb7..378b3b7 100644
--- a/src/views/iam/DomainActionForm.vue
+++ b/src/views/iam/DomainActionForm.vue
@@ -157,6 +157,10 @@
this.$pollJob({
jobId,
successMethod: result => {
+ if (this.action.api === 'deleteDomain') {
+ this.$set(this.resource, 'isDel', true)
+ this.parentUpdActionData(this.resource)
+ }
this.parentFetchData()
if (action.response) {
const description = action.response(result.jobresult)
@@ -250,9 +254,6 @@
if (!hasJobId) {
this.parentUpdActionData(json)
this.parentFetchData()
- } else {
- this.$set(this.resource, 'isDel', true)
- this.parentUpdActionData(this.resource)
}
this.parentCloseAction()
}).catch(error => {
diff --git a/src/views/iam/DomainView.vue b/src/views/iam/DomainView.vue
index 287ca56..12ba40b 100644
--- a/src/views/iam/DomainView.vue
+++ b/src/views/iam/DomainView.vue
@@ -175,19 +175,16 @@
this.resource = domains[0] || {}
this.treeSelected = domains[0] || {}
}).catch(error => {
+ if ([401].includes(error.response.status)) {
+ return
+ }
+
this.$notification.error({
message: this.$t('message.request.failed'),
description: error.response.headers['x-description'],
duration: 0
})
- if ([401].includes(error.response.status)) {
- store.dispatch('Logout').then(() => {
- this.$router.push({ path: '/user/login', query: { redirect: this.$route.fullPath } })
- })
- return
- }
-
if ([405].includes(error.response.status)) {
this.$router.push({ path: '/exception/403' })
}