You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ro...@apache.org on 2020/07/27 09:53:02 UTC
[cloudstack-primate] branch master updated: src: Consolidated Bug
fixes (#539)
This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack-primate.git
The following commit(s) were added to refs/heads/master by this push:
new 486a5b0 src: Consolidated Bug fixes (#539)
486a5b0 is described below
commit 486a5b0a67195fbabd00068e087b5559f6e1b279
Author: davidjumani <dj...@gmail.com>
AuthorDate: Mon Jul 27 15:22:53 2020 +0530
src: Consolidated Bug fixes (#539)
Fixes :
- Don't allow users in UI to delete/archive events
- Fix button name in VM deployment form in its network section to Create new network
- Refresh after template / iso upload
- Making external-id mandatory in ImportBackupOffering
- Fixing visibility of assignVirtualMachineToBackupOffering
- Removing link on traffic label
- Defensive check in TrafficTypesTab
- Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded
- Changing from report bug to report issue
- Ordering projects in menu
- Changing router get health check results
- Show configureHAForHost based on hypervisor
- Fix scale and migrate router
- Fix scale and migrate systemvm
- Fix show actionbutton for assignVirtualMachineToBackupOffering
- Fix show actionbutton for stopKubernetesCluster
- Fix show actions for volumes
- Fix show actions for snapshots
- Fix show actions for vm snapshots
- Fix show actions for backups
- Adding loading for tags and annotations
- Enter to submit advanced search
- Fixing show Project instead of account when passed as projectaccount passed in account field
- Show project name instead of displaytext
- Fixing template and iso actions
- Fixing tags with projectid
- Fix security groups ingress/egress rules view
- Removing redundant allocationstate from zones
- Adding managedstate to clusters
- Adding capacity tab to clusters and pods
- Adding routerlink to events in dashboard
- Set autofocus to username in login
---
src/components/header/ProjectMenu.vue | 13 +-
src/components/view/ActionButton.vue | 4 +-
src/components/view/DetailsTab.vue | 12 +-
src/components/view/InfoCard.vue | 177 ++++++++++++---------
src/components/view/ListView.vue | 20 +--
src/components/view/SearchView.vue | 1 +
src/config/section/compute.js | 4 +-
src/config/section/event.js | 8 +-
src/config/section/image.js | 48 +++---
src/config/section/infra/clusters.js | 5 +-
src/config/section/infra/hosts.js | 1 +
src/config/section/infra/pods.js | 7 +
src/config/section/infra/routers.js | 4 +
src/config/section/infra/systemVms.js | 8 +-
src/config/section/infra/zones.js | 4 +-
src/config/section/storage.js | 25 ++-
src/locales/en.json | 6 +-
src/store/modules/user.js | 16 +-
src/utils/request.js | 6 +-
src/views/auth/Login.vue | 1 +
src/views/compute/wizard/NetworkSelection.vue | 2 +-
src/views/dashboard/CapacityDashboard.vue | 2 +-
src/views/dashboard/UsageDashboard.vue | 2 +-
src/views/image/IsoZones.vue | 8 +-
src/views/image/RegisterOrUploadIso.vue | 6 +-
src/views/image/RegisterOrUploadTemplate.vue | 5 +-
src/views/image/TemplateZones.vue | 8 +-
.../{zone/ZoneResources.vue => Resources.vue} | 9 +-
src/views/infra/network/TrafficTypesTab.vue | 6 +-
src/views/infra/routers/RouterHealthCheck.vue | 2 +-
src/views/network/IngressEgressRuleConfigure.vue | 11 +-
src/views/offering/ImportBackupOffering.vue | 4 +-
32 files changed, 272 insertions(+), 163 deletions(-)
diff --git a/src/components/header/ProjectMenu.vue b/src/components/header/ProjectMenu.vue
index 4e1314d..f55f963 100644
--- a/src/components/header/ProjectMenu.vue
+++ b/src/components/header/ProjectMenu.vue
@@ -48,6 +48,7 @@
<script>
import store from '@/store'
import { api } from '@/api'
+import _ from 'lodash'
export default {
name: 'ProjectMenu',
@@ -66,20 +67,20 @@ export default {
return
}
var page = 1
+ const projects = []
const getNextPage = () => {
this.loading = true
api('listProjects', { listAll: true, details: 'min', page: page, pageSize: 500 }).then(json => {
- if (page === 1) {
- this.projects = [{ name: this.$t('label.default.view') }]
- }
if (json && json.listprojectsresponse && json.listprojectsresponse.project) {
- this.projects.push(...json.listprojectsresponse.project)
+ projects.push(...json.listprojectsresponse.project)
}
- if (this.projects.length - 1 < json.listprojectsresponse.count) {
+ if (projects.length < json.listprojectsresponse.count) {
page++
getNextPage()
}
}).finally(() => {
+ this.projects = _.orderBy(projects, ['displaytext'], ['asc'])
+ this.projects.unshift({ name: this.$t('label.default.view') })
this.loading = false
})
}
@@ -92,7 +93,7 @@ export default {
const project = this.projects[index]
this.$store.dispatch('SetProject', project)
this.$store.dispatch('ToggleTheme', project.id === undefined ? 'light' : 'dark')
- this.$message.success(`Switched to "${project.name}"`)
+ this.$message.success(`Switched to "${project.displaytext}"`)
if (this.$route.name !== 'dashboard') {
this.$router.push({ name: 'dashboard' })
}
diff --git a/src/components/view/ActionButton.vue b/src/components/view/ActionButton.vue
index 596a919..6aa57a8 100644
--- a/src/components/view/ActionButton.vue
+++ b/src/components/view/ActionButton.vue
@@ -32,7 +32,7 @@
:count="actionBadge[action.api] ? actionBadge[action.api].badgeNum : 0"
v-if="action.api in $store.getters.apis &&
action.showBadge && (
- (!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0))) ||
+ (!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) ||
(dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true))
)" >
<a-button
@@ -49,7 +49,7 @@
<a-button
v-if="action.api in $store.getters.apis &&
!action.showBadge && (
- (!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0))) ||
+ (!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) ||
(dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true))
)"
:icon="action.icon"
diff --git a/src/components/view/DetailsTab.vue b/src/components/view/DetailsTab.vue
index f074118..12f623d 100644
--- a/src/components/view/DetailsTab.vue
+++ b/src/components/view/DetailsTab.vue
@@ -18,7 +18,7 @@
<template>
<a-list
size="small"
- :dataSource="$route.meta.details">
+ :dataSource="projectname ? [...$route.meta.details.filter(x => x !== 'account'), 'projectname'] : $route.meta.details">
<a-list-item slot="renderItem" slot-scope="item" v-if="item in resource">
<div>
<strong>{{ item === 'service' ? $t('label.supportedservices') : $t('label.' + String(item).toLowerCase()) }}</strong>
@@ -73,7 +73,8 @@ export default {
data () {
return {
dedicatedRoutes: ['zone', 'pod', 'cluster', 'host'],
- dedicatedSectionActive: false
+ dedicatedSectionActive: false,
+ projectname: ''
}
},
mounted () {
@@ -83,6 +84,13 @@ export default {
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
},
watch: {
+ resource (newItem) {
+ this.resource = newItem
+ if ('account' in this.resource && this.resource.account.startsWith('PrjAcct-')) {
+ this.projectname = this.resource.account.substring(this.resource.account.indexOf('-') + 1, this.resource.account.lastIndexOf('-'))
+ this.resource.projectname = this.projectname
+ }
+ },
$route () {
this.dedicatedSectionActive = this.dedicatedRoutes.includes(this.$route.meta.name)
}
diff --git a/src/components/view/InfoCard.vue b/src/components/view/InfoCard.vue
index 0154f62..8e9a020 100644
--- a/src/components/view/InfoCard.vue
+++ b/src/components/view/InfoCard.vue
@@ -76,7 +76,7 @@
<a-divider/>
- <div class="resource-detail-item" v-if="resource.state || resource.status">
+ <div class="resource-detail-item" v-if="(resource.state || resource.status) && $route.meta.name !== 'zone'">
<div class="resource-detail-item__label">{{ $t('label.status') }}</div>
<div class="resource-detail-item__details">
<status class="status" :text="resource.state || resource.status" displayText/>
@@ -288,11 +288,12 @@
<span v-else>{{ resource.ipaddress }}</span>
</div>
</div>
- <div class="resource-detail-item" v-if="resource.projectid">
+ <div class="resource-detail-item" v-if="resource.projectid || resource.projectname">
<div class="resource-detail-item__label">{{ $t('label.project') }}</div>
<div class="resource-detail-item__details">
<a-icon type="project" />
- <router-link :to="{ path: '/project/' + resource.projectid }">{{ resource.project || resource.projectname || resource.projectid }}</router-link>
+ <router-link v-if="resource.projectid" :to="{ path: '/project/' + resource.projectid }">{{ resource.project || resource.projectname || resource.projectid }}</router-link>
+ <router-link v-else :to="{ path: '/project', query: { name: resource.projectname }}">{{ resource.projectname }}</router-link>
</div>
</div>
@@ -458,7 +459,7 @@
<span v-else>{{ resource.zone || resource.zonename || resource.zoneid }}</span>
</div>
</div>
- <div class="resource-detail-item" v-if="resource.account">
+ <div class="resource-detail-item" v-if="resource.account && !resource.account.startsWith('PrjAcct-')">
<div class="resource-detail-item__label">{{ $t('label.account') }}</div>
<div class="resource-detail-item__details">
<a-icon type="user" />
@@ -551,88 +552,89 @@
<div class="account-center-tags" v-if="resourceType && 'listTags' in $store.getters.apis">
<a-divider/>
- <div class="title">{{ $t('label.tags') }}</div>
- <div>
- <template v-for="(tag, index) in tags">
- <a-tag :key="index" :closable="'deleteTags' in $store.getters.apis" :afterClose="() => handleDeleteTag(tag)">
- {{ tag.key }} = {{ tag.value }}
+ <a-spin :spinning="loadingTags">
+ <div class="title">{{ $t('label.tags') }}</div>
+ <div>
+ <template v-for="(tag, index) in tags">
+ <a-tag :key="index" :closable="isAdminOrOwner() && 'deleteTags' in $store.getters.apis" :afterClose="() => handleDeleteTag(tag)">
+ {{ tag.key }} = {{ tag.value }}
+ </a-tag>
+ </template>
+
+ <div v-if="inputVisible">
+ <a-input-group
+ type="text"
+ size="small"
+ @blur="handleInputConfirm"
+ @keyup.enter="handleInputConfirm"
+ compact>
+ <a-input ref="input" :value="inputKey" @change="handleKeyChange" style="width: 30%; text-align: center" :placeholder="$t('label.key')" />
+ <a-input style=" width: 30px; border-left: 0; pointer-events: none; backgroundColor: #fff" placeholder="=" disabled />
+ <a-input :value="inputValue" @change="handleValueChange" style="width: 30%; text-align: center; border-left: 0" :placeholder="$t('label.value')" />
+ <a-button shape="circle" size="small" @click="handleInputConfirm">
+ <a-icon type="check"/>
+ </a-button>
+ <a-button shape="circle" size="small" @click="inputVisible=false">
+ <a-icon type="close"/>
+ </a-button>
+ </a-input-group>
+ </div>
+ <a-tag @click="showInput" style="background: #fff; borderStyle: dashed;" v-else-if="isAdminOrOwner() && 'createTags' in $store.getters.apis">
+ <a-icon type="plus" /> {{ $t('label.new.tag') }}
</a-tag>
- </template>
-
- <div v-if="inputVisible">
- <a-input-group
- type="text"
- size="small"
- @blur="handleInputConfirm"
- @keyup.enter="handleInputConfirm"
- compact>
- <a-input ref="input" :value="inputKey" @change="handleKeyChange" style="width: 30%; text-align: center" :placeholder="$t('label.key')" />
- <a-input style=" width: 30px; border-left: 0; pointer-events: none; backgroundColor: #fff" placeholder="=" disabled />
- <a-input :value="inputValue" @change="handleValueChange" style="width: 30%; text-align: center; border-left: 0" :placeholder="$t('label.value')" />
- <a-button shape="circle" size="small" @click="handleInputConfirm">
- <a-icon type="check"/>
- </a-button>
- <a-button shape="circle" size="small" @click="inputVisible=false">
- <a-icon type="close"/>
- </a-button>
- </a-input-group>
</div>
- <a-tag @click="showInput" style="background: #fff; borderStyle: dashed;" v-else-if="'createTags' in $store.getters.apis">
- <a-icon type="plus" /> {{ $t('label.new.tag') }}
- </a-tag>
- </div>
+ </a-spin>
</div>
<div class="account-center-team" v-if="annotationType && 'listAnnotations' in $store.getters.apis">
<a-divider :dashed="true"/>
- <div class="title">
- {{ $t('label.comments') }} ({{ notes.length }})
- </div>
- <a-list
- v-if="notes.length"
- :dataSource="notes"
- itemLayout="horizontal"
- size="small"
- >
- <a-list-item slot="renderItem" slot-scope="item">
- <a-comment
- :content="item.annotation"
- :datetime="item.created"
- >
+ <a-spin :spinning="loadingAnnotations">
+ <div class="title">
+ {{ $t('label.comments') }} ({{ notes.length }})
+ </div>
+ <a-list
+ v-if="notes.length"
+ :dataSource="notes"
+ itemLayout="horizontal"
+ size="small" >
+ <a-list-item slot="renderItem" slot-scope="item">
+ <a-comment
+ :content="item.annotation"
+ :datetime="item.created" >
+ <a-button
+ v-if="'removeAnnotation' in $store.getters.apis"
+ slot="avatar"
+ type="danger"
+ shape="circle"
+ size="small"
+ @click="deleteNote(item)">
+ <a-icon type="delete"/>
+ </a-button>
+ </a-comment>
+ </a-list-item>
+ </a-list>
+
+ <a-comment v-if="'addAnnotation' in $store.getters.apis">
+ <a-avatar
+ slot="avatar"
+ icon="edit"
+ @click="showNotesInput = true" />
+ <div slot="content">
+ <a-textarea
+ rows="4"
+ @change="handleNoteChange"
+ :value="annotation"
+ :placeholder="$t('label.add.note')" />
<a-button
- v-if="'removeAnnotation' in $store.getters.apis"
- slot="avatar"
- type="danger"
- shape="circle"
- size="small"
- @click="deleteNote(item)">
- <a-icon type="delete"/>
+ style="margin-top: 10px"
+ @click="saveNote"
+ type="primary"
+ >
+ {{ $t('label.save') }}
</a-button>
- </a-comment>
- </a-list-item>
- </a-list>
-
- <a-comment v-if="'addAnnotation' in $store.getters.apis">
- <a-avatar
- slot="avatar"
- icon="edit"
- @click="showNotesInput = true"
- />
- <div slot="content">
- <a-textarea
- rows="4"
- @change="handleNoteChange"
- :value="annotation"
- :placeholder="$t('label.add.note')" />
- <a-button
- style="margin-top: 10px"
- @click="saveNote"
- type="primary"
- >
- {{ $t('label.save') }}
- </a-button>
- </div>
- </a-comment>
+ </div>
+ </a-comment>
+ </a-spin>
</div>
</a-card>
</a-spin>
@@ -681,7 +683,9 @@ export default {
notes: [],
annotation: '',
showKeys: false,
- showNotesInput: false
+ showNotesInput: false,
+ loadingTags: false,
+ loadingAnnotations: false
}
},
watch: {
@@ -746,6 +750,7 @@ export default {
if (!('listTags' in this.$store.getters.apis) || !this.resource || !this.resource.id) {
return
}
+ this.loadingTags = true
this.tags = []
const params = {
listall: true,
@@ -759,19 +764,29 @@ export default {
if (json.listtagsresponse && json.listtagsresponse.tag) {
this.tags = json.listtagsresponse.tag
}
+ }).finally(() => {
+ this.loadingTags = false
})
},
getNotes () {
if (!('listAnnotations' in this.$store.getters.apis)) {
return
}
+ this.loadingAnnotations = true
this.notes = []
api('listAnnotations', { entityid: this.resource.id, entitytype: this.annotationType }).then(json => {
if (json.listannotationsresponse && json.listannotationsresponse.annotation) {
this.notes = json.listannotationsresponse.annotation
}
+ }).finally(() => {
+ this.loadingAnnotations = false
})
},
+ isAdminOrOwner () {
+ return ['Admin'].includes(this.$store.getters.userInfo.roletype) ||
+ (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
+ this.resource.project && this.resource.projectid === this.$store.getters.project.id
+ },
showInput () {
this.inputVisible = true
this.$nextTick(function () {
@@ -786,6 +801,7 @@ export default {
},
handleInputConfirm () {
const args = {}
+ this.loadingTags = true
args.resourceids = this.resource.id
args.resourcetype = this.resourceType
args['tags[0].key'] = this.inputKey
@@ -801,6 +817,7 @@ export default {
},
handleDeleteTag (tag) {
const args = {}
+ this.loadingTags = true
args.resourceids = this.resource.id
args.resourcetype = this.resourceType
args['tags[0].key'] = tag.key
@@ -817,6 +834,7 @@ export default {
if (this.annotation.length < 1) {
return
}
+ this.loadingAnnotations = true
this.showNotesInput = false
const args = {}
args.entityid = this.resource.id
@@ -829,6 +847,7 @@ export default {
this.annotation = ''
},
deleteNote (annotation) {
+ this.loadingAnnotations = true
const args = {}
args.id = annotation.id
api('removeAnnotation', args).then(json => {
diff --git a/src/components/view/ListView.vue b/src/components/view/ListView.vue
index 02048ab..fe0740d 100644
--- a/src/components/view/ListView.vue
+++ b/src/components/view/ListView.vue
@@ -104,9 +104,9 @@
<a slot="publicip" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
</a>
- <a slot="traffictype" slot-scope="text, record" href="javascript:;">
- <router-link :to="{ path: $route.path + '/' + record.id + '?physicalnetworkid=' + record.physicalnetworkid }">{{ text }}</router-link>
- </a>
+ <span slot="traffictype" slot-scope="text" href="javascript:;">
+ {{ text }}
+ </span>
<a slot="vmname" slot-scope="text, record" href="javascript:;">
<router-link :to="{ path: '/vm/' + record.virtualmachineid }">{{ text }}</router-link>
</a>
@@ -164,12 +164,14 @@
<router-link :to="{ path: '/pod/' + record.podid }">{{ text }}</router-link>
</a>
<span slot="account" slot-scope="text, record">
- <router-link
- v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`) !== '404'"
- :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link>
- <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link>
- <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
- <span v-else>{{ text }}</span>
+ <template v-if="text && !text.startsWith('PrjAcct-')">
+ <router-link
+ v-if="'quota' in record && $router.resolve(`${$route.path}/${record.account}`) !== '404'"
+ :to="{ path: `${$route.path}/${record.account}`, query: { account: record.account, domainid: record.domainid, quota: true } }">{{ text }}</router-link>
+ <router-link :to="{ path: '/account/' + record.accountid }" v-else-if="record.accountid">{{ text }}</router-link>
+ <router-link :to="{ path: '/account', query: { name: record.account, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
+ <span v-else>{{ text }}</span>
+ </template>
</span>
<span slot="domain" slot-scope="text, record" href="javascript:;">
<router-link v-if="record.domainid && !record.domainid.toString().includes(',') && $store.getters.userInfo.roletype !== 'User'" :to="{ path: '/domain/' + record.domainid }">{{ text }}</router-link>
diff --git a/src/components/view/SearchView.vue b/src/components/view/SearchView.vue
index 7319a8c..d7084e5 100644
--- a/src/components/view/SearchView.vue
+++ b/src/components/view/SearchView.vue
@@ -91,6 +91,7 @@
type="primary"
size="small"
icon="search"
+ html-type="submit"
@click="handleSubmit">{{ $t('label.search') }}</a-button>
</div>
</a-form>
diff --git a/src/config/section/compute.js b/src/config/section/compute.js
index 2e0723e..6a9dc58 100644
--- a/src/config/section/compute.js
+++ b/src/config/section/compute.js
@@ -177,7 +177,7 @@ export default {
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
dataView: true,
args: ['virtualmachineid', 'backupofferingid'],
- show: (record) => { return !record.backupofferingid && !['Error', 'Destroyed'].includes(record.state) && ['VMware', 'Simulator'].includes(record.hypervisor) },
+ show: (record) => { return !record.backupofferingid },
mapping: {
virtualmachineid: {
value: (record, params) => { return record.id }
@@ -441,7 +441,7 @@ export default {
label: 'label.kubernetes.cluster.stop',
docHelp: 'plugins/cloudstack-kubernetes-service.html#stopping-kubernetes-cluster',
dataView: true,
- show: (record) => { return !['Stopped'].includes(record.state) }
+ show: (record) => { return !['Stopped', 'Destroyed', 'Destroying'].includes(record.state) }
},
{
api: 'scaleKubernetesCluster',
diff --git a/src/config/section/event.js b/src/config/section/event.js
index b045332..1411fb0 100644
--- a/src/config/section/event.js
+++ b/src/config/section/event.js
@@ -44,7 +44,9 @@ export default {
ids: {
value: (record) => { return record.id }
}
- }
+ },
+ show: (record, store) => { return !['User'].includes(store.userInfo.roletype) },
+ groupShow: (record, store) => { return !['User'].includes(store.userInfo.roletype) }
},
{
api: 'deleteEvents',
@@ -60,7 +62,9 @@ export default {
ids: {
value: (record) => { return record.id }
}
- }
+ },
+ show: (record, store) => { return !['User'].includes(store.userInfo.roletype) },
+ groupShow: (record, store) => { return !['User'].includes(store.userInfo.roletype) }
}
]
}
diff --git a/src/config/section/image.js b/src/config/section/image.js
index 072121a..d8ad4cd 100644
--- a/src/config/section/image.js
+++ b/src/config/section/image.js
@@ -85,8 +85,9 @@ export default {
label: 'label.edit',
dataView: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
record.templatetype !== 'SYSTEM' &&
record.isready
},
@@ -105,8 +106,9 @@ export default {
dataView: true,
args: ['ispublic', 'isfeatured', 'isextractable'],
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
record.templatetype !== 'SYSTEM' &&
record.isready
}
@@ -119,8 +121,9 @@ export default {
docHelp: 'adminguide/templates.html#exporting-templates',
dataView: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
record.templatetype !== 'SYSTEM' &&
record.isready &&
record.isextractable
@@ -144,8 +147,9 @@ export default {
dataView: true,
popup: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
record.templatetype !== 'SYSTEM' &&
record.isready
},
@@ -208,9 +212,10 @@ export default {
label: 'label.action.edit.iso',
dataView: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
- !(record.account === 'SYSTEM' && record.domainid === 1) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
+ !(record.account === 'system' && record.domainid === 1) &&
record.isready
},
args: ['name', 'displaytext', 'bootable', 'ostypeid']
@@ -222,9 +227,10 @@ export default {
dataView: true,
args: ['ispublic', 'isfeatured', 'isextractable'],
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
- !(record.account === 'SYSTEM' && record.domainid === 1) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
+ !(record.account === 'system' && record.domainid === 1) &&
record.isready
}
},
@@ -236,9 +242,10 @@ export default {
docHelp: 'adminguide/templates.html#exporting-templates',
dataView: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
- !(record.account === 'SYSTEM' && record.domainid === 1) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
+ !(record.account === 'system' && record.domainid === 1) &&
record.isready
},
args: ['zoneid', 'mode'],
@@ -261,9 +268,10 @@ export default {
args: ['op', 'accounts', 'projectids'],
popup: true,
show: (record, store) => {
- return (['Admin'].includes(store.userInfo.roletype) ||
- (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account)) &&
- !(record.account === 'SYSTEM' && record.domainid === 1) &&
+ return (['Admin'].includes(store.userInfo.roletype) || // If admin or owner or belongs to current project
+ (record.domainid === store.userInfo.domainid && record.account === store.userInfo.account) ||
+ (record.domainid === store.userInfo.domainid && record.projectid && store.project && store.project.id && record.projectid === store.project.id)) &&
+ !(record.account === 'system' && record.domainid === 1) &&
record.isready
},
component: () => import('@/views/image/UpdateTemplateIsoPermissions')
diff --git a/src/config/section/infra/clusters.js b/src/config/section/infra/clusters.js
index 98f68a6..fcb13b0 100644
--- a/src/config/section/infra/clusters.js
+++ b/src/config/section/infra/clusters.js
@@ -32,7 +32,7 @@ export default {
fields.push('zonename')
return fields
},
- details: ['name', 'id', 'allocationstate', 'clustertype', 'hypervisortype', 'podname', 'zonename'],
+ details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 'hypervisortype', 'podname', 'zonename'],
related: [{
name: 'host',
title: 'label.hosts',
@@ -44,6 +44,9 @@ export default {
}, {
name: 'settings',
component: () => import('@/components/view/SettingsTab.vue')
+ }, {
+ name: 'resources',
+ component: () => import('@/views/infra/Resources.vue')
}],
actions: [
{
diff --git a/src/config/section/infra/hosts.js b/src/config/section/infra/hosts.js
index 8ddbef5..06e690b 100644
--- a/src/config/section/infra/hosts.js
+++ b/src/config/section/infra/hosts.js
@@ -220,6 +220,7 @@ export default {
message: 'label.ha.configure',
docHelp: 'adminguide/reliability.html#ha-for-hosts',
dataView: true,
+ show: (record) => { return ['KVM', 'Simulator'].includes(record.hypervisor) },
args: ['hostid', 'provider'],
mapping: {
hostid: {
diff --git a/src/config/section/infra/pods.js b/src/config/section/infra/pods.js
index 107fcfa..dbc9791 100644
--- a/src/config/section/infra/pods.js
+++ b/src/config/section/infra/pods.js
@@ -31,6 +31,13 @@ export default {
title: 'label.hosts',
param: 'podid'
}],
+ tabs: [{
+ name: 'details',
+ component: () => import('@/components/view/DetailsTab.vue')
+ }, {
+ name: 'resources',
+ component: () => import('@/views/infra/Resources.vue')
+ }],
actions: [
{
api: 'createPod',
diff --git a/src/config/section/infra/routers.js b/src/config/section/infra/routers.js
index 91f7357..57e12cf 100644
--- a/src/config/section/infra/routers.js
+++ b/src/config/section/infra/routers.js
@@ -103,6 +103,10 @@ export default {
mapping: {
virtualmachineid: {
value: (record) => { return record.id }
+ },
+ hostid: {
+ api: 'findHostsForMigration',
+ params: (record) => { return { virtualmachineid: record.id } }
}
}
},
diff --git a/src/config/section/infra/systemVms.js b/src/config/section/infra/systemVms.js
index 1834ade..8b3c66a 100644
--- a/src/config/section/infra/systemVms.js
+++ b/src/config/section/infra/systemVms.js
@@ -55,12 +55,12 @@ export default {
label: 'label.change.service.offering',
message: 'message.confirm.scale.up.system.vm',
dataView: true,
- show: (record) => { return record.hypervisor !== 'KVM' },
+ show: (record) => { return record.state === 'Running' && record.hypervisor === 'VMware' || record.state === 'Stopped' },
args: ['serviceofferingid'],
mapping: {
serviceofferingid: {
api: 'listServiceOfferings',
- params: (record) => { return { virtualmachineid: record.virtualmachineid, issystem: true, systemvmtype: record.systemvmtype } }
+ params: (record) => { return { virtualmachineid: record.id, issystem: true, systemvmtype: record.systemvmtype } }
}
}
},
@@ -74,6 +74,10 @@ export default {
mapping: {
virtualmachineid: {
value: (record) => { return record.id }
+ },
+ hostid: {
+ api: 'findHostsForMigration',
+ params: (record) => { return { virtualmachineid: record.id } }
}
}
},
diff --git a/src/config/section/infra/zones.js b/src/config/section/infra/zones.js
index ecfcce1..af71998 100644
--- a/src/config/section/infra/zones.js
+++ b/src/config/section/infra/zones.js
@@ -23,7 +23,7 @@ export default {
icon: 'global',
permission: ['listZonesMetrics'],
columns: () => {
- const fields = ['name', 'state', 'allocationstate', 'networktype', 'clusters']
+ const fields = ['name', 'allocationstate', 'networktype', 'clusters']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal']
if (store.getters.metrics) {
fields.push(...metricsFields)
@@ -64,7 +64,7 @@ export default {
component: () => import('@/views/infra/zone/SystemVmsTab.vue')
}, {
name: 'resources',
- component: () => import('@/views/infra/zone/ZoneResources.vue')
+ component: () => import('@/views/infra/Resources.vue')
}, {
name: 'settings',
component: () => import('@/components/view/SettingsTab.vue')
diff --git a/src/config/section/storage.js b/src/config/section/storage.js
index b7c2070..0cf1927 100644
--- a/src/config/section/storage.js
+++ b/src/config/section/storage.js
@@ -102,7 +102,7 @@ export default {
message: 'message.confirm.attach.disk',
args: ['virtualmachineid'],
dataView: true,
- show: (record) => { return record.type !== 'ROOT' && record.state !== 'Destroy' && !('virtualmachineid' in record) }
+ show: (record) => { return record.type !== 'ROOT' && ['Allocated', 'Ready', 'Uploaded'].includes(record.state) && !('virtualmachineid' in record) }
},
{
api: 'detachVolume',
@@ -110,7 +110,10 @@ export default {
label: 'label.action.detach.disk',
message: 'message.detach.disk',
dataView: true,
- show: (record) => { return record.type !== 'ROOT' && 'virtualmachineid' in record && record.virtualmachineid }
+ show: (record) => {
+ return record.type !== 'ROOT' && 'virtualmachineid' in record && record.virtualmachineid &&
+ ['Running', 'Stopped', 'Destroyed'].includes(record.vmstate)
+ }
},
{
api: 'createSnapshot',
@@ -147,7 +150,7 @@ export default {
label: 'label.action.resize.volume',
dataView: true,
popup: true,
- show: (record) => { return record.state !== 'Destroy' },
+ show: (record) => { return ['Allocated', 'Ready'].includes(record.state) },
component: () => import('@/views/storage/ResizeVolume.vue')
},
{
@@ -167,7 +170,7 @@ export default {
label: 'label.action.download.volume',
message: 'message.download.volume.confirm',
dataView: true,
- show: (record) => { return record && record.state === 'Ready' && (record.vmstate === 'Stopped' || record.virtualmachineid == null) && record.state !== 'Destroy' },
+ show: (record) => { return record && record.state === 'Ready' && (record.vmstate === 'Stopped' || record.virtualmachineid == null) },
args: ['zoneid', 'mode'],
mapping: {
zoneid: {
@@ -278,14 +281,15 @@ export default {
label: 'label.action.revert.snapshot',
message: 'message.action.revert.snapshot',
dataView: true,
- show: (record) => { return record.revertable }
+ show: (record) => { return record.state === 'BackedUp' && record.revertable }
},
{
api: 'deleteSnapshot',
icon: 'delete',
label: 'label.action.delete.snapshot',
message: 'message.action.delete.snapshot',
- dataView: true
+ dataView: true,
+ show: (record) => { return record.state !== 'Destroyed' }
}
]
},
@@ -326,6 +330,7 @@ export default {
label: 'label.action.vmsnapshot.delete',
message: 'message.action.vmsnapshot.delete',
dataView: true,
+ show: (record) => { return ['Ready', 'Expunging', 'Error'].includes(record.state) },
args: ['vmsnapshotid'],
mapping: {
vmsnapshotid: {
@@ -349,7 +354,8 @@ export default {
docHelp: 'adminguide/virtual_machines.html#restoring-vm-backups',
label: 'label.backup.restore',
message: 'message.backup.restore',
- dataView: true
+ dataView: true,
+ show: (record) => { return record.state !== 'Destroyed' }
},
{
api: 'restoreVolumeFromBackupAndAttachToVM',
@@ -357,6 +363,7 @@ export default {
label: 'label.backup.attach.restore',
message: 'message.backup.attach.restore',
dataView: true,
+ show: (record) => { return record.state !== 'Destroyed' },
popup: true,
component: () => import('@/views/storage/RestoreAttachBackupVolume.vue')
},
@@ -366,6 +373,7 @@ export default {
label: 'label.backup.offering.remove',
message: 'message.backup.offering.remove',
dataView: true,
+ show: (record) => { return record.state !== 'Destroyed' },
args: ['forced', 'virtualmachineid'],
mapping: {
forced: {
@@ -381,7 +389,8 @@ export default {
icon: 'delete',
label: 'label.delete.backup',
message: 'message.delete.backup',
- dataView: true
+ dataView: true,
+ show: (record) => { return record.state !== 'Destroyed' }
}
]
}
diff --git a/src/locales/en.json b/src/locales/en.json
index 3277611..f0831d8 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -46,7 +46,7 @@
"label.access": "Access",
"label.accesskey": "Access Key",
"label.account": "Account",
-"label.account.and.security.group": "Account, Security group",
+"label.account.and.security.group": "Account - Security group",
"label.account.details": "Account details",
"label.account.id": "Account ID",
"label.account.name": "Account Name",
@@ -602,6 +602,7 @@
"label.cpuusedghz": "Used CPU",
"label.create.account": "Create Account",
"label.create.backup": "Start Backup",
+"label.create.network": "Create New Network",
"label.create.network.gateway.description": "The gateway of the tier in the super CIDR range and not overlapping the CIDR of any other tier in this VPC.",
"label.create.network.netmask.description": "Netmask of the tier. For example, with VPC CIDR of 10.0.0.0/16 and network tier CIDR of 10.1.1.0/24, gateway is 10.1.1.1 and netmask is 255.255.255.0",
"label.create.nfs.secondary.staging.storage": "Create NFS Secondary Staging Store",
@@ -1584,6 +1585,7 @@
"label.profiledn": "Associated Profile",
"label.profilename": "Profile",
"label.project": "Project",
+"label.projectname": "Project",
"label.project.dashboard": "Project dashboard",
"label.project.ids": "Project IDs",
"label.project.invitation": "Project Invitations",
@@ -1724,7 +1726,7 @@
"label.removing.user": "Removing User",
"label.replace.acl": "Replace ACL",
"label.replace.acl.list": "Replace ACL List",
-"label.report.bug": "Report Bug",
+"label.report.bug": "Report Issue",
"label.required": "Required",
"label.requireshvm": "HVM",
"label.requiresupgrade": "Requires Upgrade",
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index abb57e5..3fa46da 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -123,7 +123,21 @@ const user = {
if (hasAuth) {
console.log('Login detected, using cached APIs')
commit('SET_APIS', cachedApis)
- resolve(cachedApis)
+
+ // Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded
+ api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
+ const result = response.listusersresponse.user[0]
+ commit('SET_INFO', result)
+ commit('SET_NAME', result.firstname + ' ' + result.lastname)
+ if ('email' in result) {
+ commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5(result.email))
+ } else {
+ commit('SET_AVATAR', 'https://www.gravatar.com/avatar/' + md5('dev@cloudstack.apache.org'))
+ }
+ resolve(cachedApis)
+ }).catch(error => {
+ reject(error)
+ })
} else {
const hide = message.loading(i18n.t('message.discovering.feature'), 0)
api('listApis').then(response => {
diff --git a/src/utils/request.js b/src/utils/request.js
index 382dcfd..0abd090 100644
--- a/src/utils/request.js
+++ b/src/utils/request.js
@@ -72,7 +72,11 @@ service.interceptors.request.use(config => {
config.params.response = 'json'
const project = Vue.ls.get(CURRENT_PROJECT)
if (!config.params.projectid && project && project.id) {
- config.params.projectid = project.id
+ if (config.params.command === 'listTags') {
+ config.params.projectid = '-1'
+ } else {
+ config.params.projectid = project.id
+ }
}
}
return config
diff --git a/src/views/auth/Login.vue b/src/views/auth/Login.vue
index aa53f53..5f95872 100644
--- a/src/views/auth/Login.vue
+++ b/src/views/auth/Login.vue
@@ -39,6 +39,7 @@
<a-input
size="large"
type="text"
+ autoFocus
:placeholder="$t('label.username')"
v-decorator="[
'username',
diff --git a/src/views/compute/wizard/NetworkSelection.vue b/src/views/compute/wizard/NetworkSelection.vue
index 4a011d0..85d3c14 100644
--- a/src/views/compute/wizard/NetworkSelection.vue
+++ b/src/views/compute/wizard/NetworkSelection.vue
@@ -23,7 +23,7 @@
v-model="filter"
@search="handleSearch" />
<a-button type="primary" @click="showCreateForm = true" style="float: right; margin-right: 5px; z-index: 8">
- {{ $t('label.add.network') }}
+ {{ $t('label.create.network') }}
</a-button>
<a-table
:loading="loading"
diff --git a/src/views/dashboard/CapacityDashboard.vue b/src/views/dashboard/CapacityDashboard.vue
index 07bb14e..6c01044 100644
--- a/src/views/dashboard/CapacityDashboard.vue
+++ b/src/views/dashboard/CapacityDashboard.vue
@@ -109,7 +109,7 @@
:key="event.id"
:color="getEventColour(event)">
<span :style="{ color: '#999' }"><small>{{ event.created }}</small></span><br/>
- <span :style="{ color: '#666' }"><small>{{ $t(event.type.toLowerCase()) }}</small></span><br/>
+ <span :style="{ color: '#666' }"><small><router-link :to="{ path: 'event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
</a-timeline-item>
</a-timeline>
diff --git a/src/views/dashboard/UsageDashboard.vue b/src/views/dashboard/UsageDashboard.vue
index a448032..b29fc8d 100644
--- a/src/views/dashboard/UsageDashboard.vue
+++ b/src/views/dashboard/UsageDashboard.vue
@@ -82,7 +82,7 @@
:key="event.id"
:color="getEventColour(event)">
<span :style="{ color: '#999' }"><small>{{ event.created }}</small></span><br/>
- <span :style="{ color: '#666' }"><small>{{ event.type }}</small></span><br/>
+ <span :style="{ color: '#666' }"><small><router-link :to="{ path: 'event/' + event.id }">{{ event.type }}</router-link></small></span><br/>
<span :style="{ color: '#aaa' }">({{ event.username }}) {{ event.description }}</span>
</a-timeline-item>
</a-timeline>
diff --git a/src/views/image/IsoZones.vue b/src/views/image/IsoZones.vue
index 91db6c5..89351f7 100644
--- a/src/views/image/IsoZones.vue
+++ b/src/views/image/IsoZones.vue
@@ -230,9 +230,11 @@ export default {
this.fetchData()
},
isActionPermitted () {
- return (['Admin'].includes(this.$store.getters.userInfo.roletype) ||
- (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account)) &&
- !(this.resource.account !== 'SYSTEM' && this.resource.domainid === 1)
+ return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
+ (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
+ (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
+ (this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Iso is ready or downloaded
+ this.resource.account !== 'system'
},
deleteIso (record) {
const params = {
diff --git a/src/views/image/RegisterOrUploadIso.vue b/src/views/image/RegisterOrUploadIso.vue
index 96fe426..f632fc6 100644
--- a/src/views/image/RegisterOrUploadIso.vue
+++ b/src/views/image/RegisterOrUploadIso.vue
@@ -293,14 +293,15 @@ export default {
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.description')
})
- this.closeAction()
}).catch(e => {
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.iso.failed.description')} - ${e}`,
duration: 0
})
+ }).finally(() => {
this.closeAction()
+ this.$emit('refresh-data')
})
},
handleSubmit (e) {
@@ -336,7 +337,6 @@ export default {
if (this.currentForm === 'Create') {
this.loading = true
api('registerIso', params).then(json => {
- this.$emit('refresh-data')
this.$notification.success({
message: 'label.action.register.iso',
description: `${this.$t('message.success.register.iso')} ${params.name}`
@@ -346,6 +346,7 @@ export default {
}).finally(() => {
this.loading = false
this.closeAction()
+ this.$emit('refresh-data')
})
} else {
if (this.fileList.length !== 1) {
@@ -366,6 +367,7 @@ export default {
this.$notifyError(error)
}).finally(() => {
this.loading = false
+ this.$emit('refresh-data')
})
}
})
diff --git a/src/views/image/RegisterOrUploadTemplate.vue b/src/views/image/RegisterOrUploadTemplate.vue
index 9ee0348..f0ad376 100644
--- a/src/views/image/RegisterOrUploadTemplate.vue
+++ b/src/views/image/RegisterOrUploadTemplate.vue
@@ -479,13 +479,14 @@ export default {
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.template.description')
})
- this.closeAction()
}).catch(e => {
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.template.failed.description')} - ${e}`,
duration: 0
})
+ }).finally(() => {
+ this.$emit('refresh-data')
this.closeAction()
})
},
@@ -852,7 +853,6 @@ export default {
if (this.currentForm === 'Create') {
this.loading = true
api('registerTemplate', params).then(json => {
- this.$emit('refresh-data')
this.$notification.success({
message: this.$t('label.register.template'),
description: `${this.$t('message.success.register.template')} ${params.name}`
@@ -861,6 +861,7 @@ export default {
this.$notifyError(error)
}).finally(() => {
this.loading = false
+ this.$emit('refresh-data')
this.closeAction()
})
} else {
diff --git a/src/views/image/TemplateZones.vue b/src/views/image/TemplateZones.vue
index e8b0a1c..00e7af2 100644
--- a/src/views/image/TemplateZones.vue
+++ b/src/views/image/TemplateZones.vue
@@ -240,9 +240,11 @@ export default {
this.fetchData()
},
isActionPermitted () {
- return (['Admin'].includes(this.$store.getters.userInfo.roletype) ||
- (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account)) &&
- this.resource.isready && this.resource.templatetype !== 'SYSTEM'
+ return (['Admin'].includes(this.$store.getters.userInfo.roletype) || // If admin or owner or belongs to current project
+ (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.account === this.$store.getters.userInfo.account) ||
+ (this.resource.domainid === this.$store.getters.userInfo.domainid && this.resource.projectid && this.$store.getters.project && this.$store.getters.project.id && this.resource.projectid === this.$store.getters.project.id)) &&
+ (this.resource.isready || !this.resource.status || this.resource.status.indexOf('Downloaded') === -1) && // Template is ready or downloaded
+ this.resource.templatetype !== 'SYSTEM'
},
deleteTemplate () {
const params = {
diff --git a/src/views/infra/zone/ZoneResources.vue b/src/views/infra/Resources.vue
similarity index 96%
rename from src/views/infra/zone/ZoneResources.vue
rename to src/views/infra/Resources.vue
index 0531fea..aba42ef 100644
--- a/src/views/infra/zone/ZoneResources.vue
+++ b/src/views/infra/Resources.vue
@@ -41,7 +41,7 @@
import { api } from '@/api'
export default {
- name: 'ZoneResources',
+ name: 'Resources',
props: {
resource: {
type: Object,
@@ -66,10 +66,11 @@ export default {
},
methods: {
fetchData () {
+ const entity = this.$route.meta.name + 'id'
+ const params = {}
+ params[entity] = this.resource.id
this.fetchLoading = true
- api('listCapacity', {
- zoneid: this.resource.id
- }).then(response => {
+ api('listCapacity', params).then(response => {
this.resourcesList = response.listcapacityresponse.capacity
this.animatePercentVals()
}).catch(error => {
diff --git a/src/views/infra/network/TrafficTypesTab.vue b/src/views/infra/network/TrafficTypesTab.vue
index 33b04eb..300511c 100644
--- a/src/views/infra/network/TrafficTypesTab.vue
+++ b/src/views/infra/network/TrafficTypesTab.vue
@@ -111,7 +111,11 @@ export default {
isSystem: true,
zoneId: this.resource.zoneid
}).then(json => {
- this.publicNetwork = json.listnetworksresponse.network[0] || {}
+ if (json.listnetworksresponse && json.listnetworksresponse.network && json.listnetworksresponse.network.length > 0) {
+ this.publicNetwork = json.listnetworksresponse.network[0]
+ } else {
+ this.publicNetwork = {}
+ }
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
diff --git a/src/views/infra/routers/RouterHealthCheck.vue b/src/views/infra/routers/RouterHealthCheck.vue
index 489fe56..31fd45b 100644
--- a/src/views/infra/routers/RouterHealthCheck.vue
+++ b/src/views/infra/routers/RouterHealthCheck.vue
@@ -22,7 +22,7 @@
banner
:message="$t('message.action.router.health.checks.disabled.warning')" />
<div v-else>
- <a-button :disabled="!('getRouterHealthCheckResults' in $store.getters.apis)" type="primary" icon="download" style="width: 100%; margin-bottom: 15px" @click="showGetHelathCheck">
+ <a-button :disabled="!('getRouterHealthCheckResults' in $store.getters.apis)" type="primary" icon="play-circle" style="width: 100%; margin-bottom: 15px" @click="showGetHelathCheck">
{{ $t('label.action.router.health.checks') }}
</a-button>
<a-table
diff --git a/src/views/network/IngressEgressRuleConfigure.vue b/src/views/network/IngressEgressRuleConfigure.vue
index 1a34351..0d3c4c7 100644
--- a/src/views/network/IngressEgressRuleConfigure.vue
+++ b/src/views/network/IngressEgressRuleConfigure.vue
@@ -62,13 +62,16 @@
<div class="form__label">{{ $t('label.cidr') }}</div>
<a-input v-model="newRule.cidrlist"></a-input>
</div>
- <div class="form__item" v-if="addType === 'account'">
- <div class="form__label">{{ $t('label.account.and.security.group') }}</div>
- <div style="display:flex;">
+ <template v-if="addType === 'account'">
+ <div class="form__item">
+ <div class="form__label">{{ $t('label.account') }}</div>
<a-input v-model="newRule.usersecuritygrouplist.account" style="margin-right: 10px;"></a-input>
+ </div>
+ <div class="form__item">
+ <div class="form__label">{{ $t('label.securitygroup') }}</div>
<a-input v-model="newRule.usersecuritygrouplist.group"></a-input>
</div>
- </div>
+ </template>
<div class="form__item" style="flex: 0">
<a-button :disabled="!('authorizeSecurityGroupInress' in $store.getters.apis) && !('authorizeSecurityGroupEgress' in $store.getters.apis)" type="primary" @click="handleAddRule">{{ $t('label.add') }}</a-button>
</div>
diff --git a/src/views/offering/ImportBackupOffering.vue b/src/views/offering/ImportBackupOffering.vue
index cff2e75..8af621a 100644
--- a/src/views/offering/ImportBackupOffering.vue
+++ b/src/views/offering/ImportBackupOffering.vue
@@ -74,7 +74,9 @@
</span>
<a-select
allowClear
- v-decorator="['externalid'] "
+ v-decorator="['externalid', {
+ rules: [{ required: true, message: `${this.$t('message.error.select')}` }]
+ }] "
:loading="externals.loading">
<a-select-option v-for="opt in externals.opts" :key="opt.id">
{{ opt.name }}