mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
src: assorted bug fixes (#564)
Fixes : - Fixing scale router - Fixing account actions - Fixing user actions - Adding message for create vm backup - Fix default allowuserdrivenbackups in ImportBackupOfferings - Fix typo in TakeSnapshot - Ensuring zone mandatory in upload template - Adding securitygroup to instacetab - Adding related vms to routers - Adding makeredundant to restart network - Fixing no key in listview - Link to ipaddress only if router path is publicip - Show vpc routers only to admin - Fix restartVPC args - Fix storage action visibility - Reorder routes to match legacy - Reorder cluster tabs - Fix number input width - Fix create vpc - List events also on fetchlatest - Fix show domain actions - Removing resource admin from default roles - Fix missing store - Adding createVPC view - Adding attachiso view Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
fa934769d6
commit
3979f1f5d5
@ -32,7 +32,7 @@
|
|||||||
:count="actionBadge[action.api] ? actionBadge[action.api].badgeNum : 0"
|
:count="actionBadge[action.api] ? actionBadge[action.api].badgeNum : 0"
|
||||||
v-if="action.api in $store.getters.apis &&
|
v-if="action.api in $store.getters.apis &&
|
||||||
action.showBadge && (
|
action.showBadge && (
|
||||||
(!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) ||
|
(!dataView && ((action.listView && ('show' in action ? action.show(resource, $store.getters) : true)) || (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))
|
(dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true))
|
||||||
)" >
|
)" >
|
||||||
<a-button
|
<a-button
|
||||||
@ -50,7 +50,7 @@
|
|||||||
<a-button
|
<a-button
|
||||||
v-if="action.api in $store.getters.apis &&
|
v-if="action.api in $store.getters.apis &&
|
||||||
!action.showBadge && (
|
!action.showBadge && (
|
||||||
(!dataView && (action.listView || (action.groupAction && selectedRowKeys.length > 0 && ('groupShow' in action ? action.show(resource, $store.getters) : true)))) ||
|
(!dataView && ((action.listView && ('show' in action ? action.show(resource, $store.getters) : true)) || (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))
|
(dataView && action.dataView && ('show' in action ? action.show(resource, $store.getters) : true))
|
||||||
)"
|
)"
|
||||||
:icon="action.icon"
|
:icon="action.icon"
|
||||||
|
|||||||
@ -519,7 +519,7 @@
|
|||||||
<div v-for="item in $route.meta.related" :key="item.path">
|
<div v-for="item in $route.meta.related" :key="item.path">
|
||||||
<router-link
|
<router-link
|
||||||
v-if="$router.resolve('/' + item.name).route.name !== '404'"
|
v-if="$router.resolve('/' + item.name).route.name !== '404'"
|
||||||
:to="{ path: '/' + item.name + '?' + item.param + '=' + (item.param === 'account' ? resource.name + '&domainid=' + resource.domainid : resource.id) }">
|
:to="{ path: '/' + item.name + '?' + item.param + '=' + (item.value ? resource[item.value] : item.param === 'account' ? resource.name + '&domainid=' + resource.domainid : resource.id) }">
|
||||||
<a-button style="margin-right: 10px" :icon="$router.resolve('/' + item.name).route.meta.icon" >
|
<a-button style="margin-right: 10px" :icon="$router.resolve('/' + item.name).route.meta.icon" >
|
||||||
{{ $t('label.view') + ' ' + $t(item.title) }}
|
{{ $t('label.view') + ' ' + $t(item.title) }}
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|||||||
@ -27,10 +27,10 @@
|
|||||||
<a-table
|
<a-table
|
||||||
size="small"
|
size="small"
|
||||||
:columns="fetchColumns()"
|
:columns="fetchColumns()"
|
||||||
:dataSource="items"
|
:dataSource="dataSource"
|
||||||
:rowKey="item => item.id"
|
:rowKey="item => item.id"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="false"
|
:pagination="defaultPagination"
|
||||||
@change="handleTableChange"
|
@change="handleTableChange"
|
||||||
@handle-search-filter="handleTableChange" >
|
@handle-search-filter="handleTableChange" >
|
||||||
|
|
||||||
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
</a-table>
|
</a-table>
|
||||||
|
|
||||||
<div style="display: block; text-align: right; margin-top: 10px;">
|
<div v-if="!defaultPagination" style="display: block; text-align: right; margin-top: 10px;">
|
||||||
<a-pagination
|
<a-pagination
|
||||||
size="small"
|
size="small"
|
||||||
:current="options.page"
|
:current="options.page"
|
||||||
@ -88,7 +88,7 @@ export default {
|
|||||||
},
|
},
|
||||||
apiName: {
|
apiName: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true
|
default: ''
|
||||||
},
|
},
|
||||||
routerlinks: {
|
routerlinks: {
|
||||||
type: Function,
|
type: Function,
|
||||||
@ -96,7 +96,7 @@ export default {
|
|||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
default: () => {}
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -105,14 +105,19 @@ export default {
|
|||||||
showSearch: {
|
showSearch: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
items: [],
|
dataSource: [],
|
||||||
total: 0,
|
total: 0,
|
||||||
filter: '',
|
filter: '',
|
||||||
|
defaultPagination: false,
|
||||||
options: {
|
options: {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -126,6 +131,11 @@ export default {
|
|||||||
this.fetchData()
|
this.fetchData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
items (newItem, oldItem) {
|
||||||
|
if (newItem) {
|
||||||
|
this.dataSource = newItem
|
||||||
|
}
|
||||||
|
},
|
||||||
'$i18n.locale' (to, from) {
|
'$i18n.locale' (to, from) {
|
||||||
if (to !== from) {
|
if (to !== from) {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
@ -137,6 +147,14 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchData () {
|
fetchData () {
|
||||||
|
if (this.items && this.items.length > 0) {
|
||||||
|
this.dataSource = this.items
|
||||||
|
this.defaultPagination = {
|
||||||
|
showSizeChanger: true,
|
||||||
|
pageSizeOptions: this.mixinDevice === 'desktop' ? ['20', '50', '100', '500'] : ['10', '20', '50', '100', '500']
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
this.loading = true
|
this.loading = true
|
||||||
var params = { ...this.params, ...this.options }
|
var params = { ...this.params, ...this.options }
|
||||||
params.listall = true
|
params.listall = true
|
||||||
@ -159,9 +177,9 @@ export default {
|
|||||||
objectName = key
|
objectName = key
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.items = json[responseName][objectName]
|
this.dataSource = json[responseName][objectName]
|
||||||
if (!this.items || this.items.length === 0) {
|
if (!this.dataSource || this.dataSource.length === 0) {
|
||||||
this.items = []
|
this.dataSource = []
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:columns="isOrderUpdatable() ? columns : columns.filter(x => x.dataIndex !== 'order')"
|
:columns="isOrderUpdatable() ? columns : columns.filter(x => x.dataIndex !== 'order')"
|
||||||
:dataSource="items"
|
:dataSource="items"
|
||||||
:rowKey="record => record.id || record.name || record.usageType"
|
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:rowSelection="['vm', 'event', 'alert'].includes($route.name) ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
|
:rowSelection="['vm', 'event', 'alert'].includes($route.name) ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange} : null"
|
||||||
:rowClassName="getRowClassName"
|
:rowClassName="getRowClassName"
|
||||||
@ -99,13 +99,14 @@
|
|||||||
<router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
|
<router-link :to="{ path: '/accountuser', query: { username: record.username, domainid: record.domainid } }" v-else-if="$store.getters.userInfo.roletype !== 'User'">{{ text }}</router-link>
|
||||||
<span v-else>{{ text }}</span>
|
<span v-else>{{ text }}</span>
|
||||||
</span>
|
</span>
|
||||||
<a slot="ipaddress" slot-scope="text, record" href="javascript:;">
|
<span slot="ipaddress" slot-scope="text, record" href="javascript:;">
|
||||||
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
<router-link v-if="$route.path === '/publicip'" :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
||||||
|
<span v-else>{{ text }}</span>
|
||||||
<span v-if="record.issourcenat">
|
<span v-if="record.issourcenat">
|
||||||
|
|
||||||
<a-tag>source-nat</a-tag>
|
<a-tag>source-nat</a-tag>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</span>
|
||||||
<a slot="publicip" slot-scope="text, record" href="javascript:;">
|
<a slot="publicip" slot-scope="text, record" href="javascript:;">
|
||||||
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
<router-link :to="{ path: $route.path + '/' + record.id }">{{ text }}</router-link>
|
||||||
</a>
|
</a>
|
||||||
@ -438,59 +439,56 @@ export default {
|
|||||||
this.parentToggleLoading()
|
this.parentToggleLoading()
|
||||||
const apiString = this.getUpdateApi()
|
const apiString = this.getUpdateApi()
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
api(apiString, {
|
api(apiString, {
|
||||||
id,
|
id,
|
||||||
sortKey: index
|
sortKey: index
|
||||||
}).catch(error => {
|
}).then((response) => {
|
||||||
console.error(error)
|
resolve(response)
|
||||||
|
}).catch((reason) => {
|
||||||
|
reject(reason)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateOrder (data) {
|
||||||
|
const promises = []
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
promises.push(this.handleUpdateOrder(item.id, index + 1))
|
||||||
|
})
|
||||||
|
Promise.all(promises).catch((reason) => {
|
||||||
|
console.log(reason)
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.parentFetchData()
|
|
||||||
this.parentToggleLoading()
|
this.parentToggleLoading()
|
||||||
|
this.parentFetchData()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
moveItemUp (record) {
|
moveItemUp (record) {
|
||||||
const data = this.items
|
const data = this.items
|
||||||
const index = data.findIndex(item => item.id === record.id)
|
const index = data.findIndex(item => item.id === record.id)
|
||||||
if (index === 0) return
|
if (index === 0) return
|
||||||
|
|
||||||
data.splice(index - 1, 0, data.splice(index, 1)[0])
|
data.splice(index - 1, 0, data.splice(index, 1)[0])
|
||||||
|
this.updateOrder(data)
|
||||||
data.forEach((item, index) => {
|
|
||||||
this.handleUpdateOrder(item.id, index + 1)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
moveItemDown (record) {
|
moveItemDown (record) {
|
||||||
const data = this.items
|
const data = this.items
|
||||||
const index = data.findIndex(item => item.id === record.id)
|
const index = data.findIndex(item => item.id === record.id)
|
||||||
if (index === data.length - 1) return
|
if (index === data.length - 1) return
|
||||||
|
|
||||||
data.splice(index + 1, 0, data.splice(index, 1)[0])
|
data.splice(index + 1, 0, data.splice(index, 1)[0])
|
||||||
|
this.updateOrder(data)
|
||||||
data.forEach((item, index) => {
|
|
||||||
this.handleUpdateOrder(item.id, index + 1)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
moveItemTop (record) {
|
moveItemTop (record) {
|
||||||
const data = this.items
|
const data = this.items
|
||||||
const index = data.findIndex(item => item.id === record.id)
|
const index = data.findIndex(item => item.id === record.id)
|
||||||
if (index === 0) return
|
if (index === 0) return
|
||||||
|
|
||||||
data.unshift(data.splice(index, 1)[0])
|
data.unshift(data.splice(index, 1)[0])
|
||||||
|
this.updateOrder(data)
|
||||||
data.forEach((item, index) => {
|
|
||||||
this.handleUpdateOrder(item.id, index + 1)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
moveItemBottom (record) {
|
moveItemBottom (record) {
|
||||||
const data = this.items
|
const data = this.items
|
||||||
const index = data.findIndex(item => item.id === record.id)
|
const index = data.findIndex(item => item.id === record.id)
|
||||||
if (index === data.length - 1) return
|
if (index === data.length - 1) return
|
||||||
|
|
||||||
data.push(data.splice(index, 1)[0])
|
data.push(data.splice(index, 1)[0])
|
||||||
|
this.updateOrder(data)
|
||||||
data.forEach((item, index) => {
|
|
||||||
this.handleUpdateOrder(item.id, index + 1)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
editTariffValue (record) {
|
editTariffValue (record) {
|
||||||
this.parentEditTariffAction(true, record)
|
this.parentEditTariffAction(true, record)
|
||||||
|
|||||||
@ -218,9 +218,9 @@ export function asyncRouterMap () {
|
|||||||
generateRouterMap(event),
|
generateRouterMap(event),
|
||||||
generateRouterMap(project),
|
generateRouterMap(project),
|
||||||
generateRouterMap(user),
|
generateRouterMap(user),
|
||||||
|
generateRouterMap(role),
|
||||||
generateRouterMap(account),
|
generateRouterMap(account),
|
||||||
generateRouterMap(domain),
|
generateRouterMap(domain),
|
||||||
generateRouterMap(role),
|
|
||||||
generateRouterMap(infra),
|
generateRouterMap(infra),
|
||||||
generateRouterMap(offering),
|
generateRouterMap(offering),
|
||||||
generateRouterMap(config),
|
generateRouterMap(config),
|
||||||
|
|||||||
@ -94,6 +94,7 @@ export default {
|
|||||||
label: 'label.action.update.resource.count',
|
label: 'label.action.update.resource.count',
|
||||||
message: 'message.update.resource.count',
|
message: 'message.update.resource.count',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
|
show: (record, store) => { return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) },
|
||||||
args: ['account', 'domainid'],
|
args: ['account', 'domainid'],
|
||||||
mapping: {
|
mapping: {
|
||||||
account: {
|
account: {
|
||||||
@ -110,7 +111,11 @@ export default {
|
|||||||
label: 'label.action.enable.account',
|
label: 'label.action.enable.account',
|
||||||
message: 'message.enable.account',
|
message: 'message.enable.account',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'disabled' || record.state === 'locked' },
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1) &&
|
||||||
|
(record.state === 'disabled' || record.state === 'locked')
|
||||||
|
},
|
||||||
params: { lock: 'false' }
|
params: { lock: 'false' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -119,7 +124,11 @@ export default {
|
|||||||
label: 'label.action.disable.account',
|
label: 'label.action.disable.account',
|
||||||
message: 'message.disable.account',
|
message: 'message.disable.account',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'enabled' },
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1) &&
|
||||||
|
record.state === 'enabled'
|
||||||
|
},
|
||||||
args: ['lock'],
|
args: ['lock'],
|
||||||
mapping: {
|
mapping: {
|
||||||
lock: {
|
lock: {
|
||||||
@ -133,7 +142,11 @@ export default {
|
|||||||
label: 'label.action.lock.account',
|
label: 'label.action.lock.account',
|
||||||
message: 'message.lock.account',
|
message: 'message.lock.account',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'enabled' },
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1) &&
|
||||||
|
record.state === 'enabled'
|
||||||
|
},
|
||||||
args: ['lock'],
|
args: ['lock'],
|
||||||
mapping: {
|
mapping: {
|
||||||
lock: {
|
lock: {
|
||||||
@ -163,7 +176,10 @@ export default {
|
|||||||
label: 'label.action.delete.account',
|
label: 'label.action.delete.account',
|
||||||
message: 'message.delete.account',
|
message: 'message.delete.account',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
hidden: (record) => { return record.name === 'admin' }
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.name === 'admin' && record.accounttype === 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,6 +188,7 @@ export default {
|
|||||||
api: 'createBackup',
|
api: 'createBackup',
|
||||||
icon: 'cloud-upload',
|
icon: 'cloud-upload',
|
||||||
label: 'label.create.backup',
|
label: 'label.create.backup',
|
||||||
|
message: 'message.backup.create',
|
||||||
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
docHelp: 'adminguide/virtual_machines.html#creating-vm-backups',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['virtualmachineid'],
|
args: ['virtualmachineid'],
|
||||||
@ -237,17 +238,9 @@ export default {
|
|||||||
label: 'label.action.attach.iso',
|
label: 'label.action.attach.iso',
|
||||||
docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm',
|
docHelp: 'adminguide/templates.html#attaching-an-iso-to-a-vm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['id', 'virtualmachineid'],
|
popup: true,
|
||||||
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid },
|
show: (record) => { return ['Running', 'Stopped'].includes(record.state) && !record.isoid },
|
||||||
mapping: {
|
component: () => import('@/views/compute/AttachIso.vue')
|
||||||
id: {
|
|
||||||
api: 'listIsos',
|
|
||||||
params: (record) => { return { zoneid: record.zoneid } }
|
|
||||||
},
|
|
||||||
virtualmachineid: {
|
|
||||||
value: (record, params) => { return record.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'detachIso',
|
api: 'detachIso',
|
||||||
|
|||||||
@ -77,7 +77,17 @@ export default {
|
|||||||
label: 'label.action.edit.domain',
|
label: 'label.action.edit.domain',
|
||||||
listView: true,
|
listView: true,
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['name', 'networkdomain']
|
args: (record) => {
|
||||||
|
var fields = ['networkdomain']
|
||||||
|
if (record.name !== 'ROOT') {
|
||||||
|
fields.unshift('name')
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
},
|
||||||
|
show: (record, store) => {
|
||||||
|
return ['Admin'].includes(store.userInfo.roletype) ||
|
||||||
|
['DomainAdmin'].includes(store.userInfo.roletype) && record.domainid !== store.userInfo.domainid
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'updateResourceCount',
|
api: 'updateResourceCount',
|
||||||
@ -119,7 +129,11 @@ export default {
|
|||||||
label: 'label.action.delete.domain',
|
label: 'label.action.delete.domain',
|
||||||
listView: true,
|
listView: true,
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.level !== 0 },
|
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
|
||||||
|
},
|
||||||
args: ['cleanup']
|
args: ['cleanup']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -41,12 +41,12 @@ export default {
|
|||||||
tabs: [{
|
tabs: [{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
component: () => import('@/components/view/DetailsTab.vue')
|
component: () => import('@/components/view/DetailsTab.vue')
|
||||||
}, {
|
|
||||||
name: 'settings',
|
|
||||||
component: () => import('@/components/view/SettingsTab.vue')
|
|
||||||
}, {
|
}, {
|
||||||
name: 'resources',
|
name: 'resources',
|
||||||
component: () => import('@/views/infra/Resources.vue')
|
component: () => import('@/views/infra/Resources.vue')
|
||||||
|
}, {
|
||||||
|
name: 'settings',
|
||||||
|
component: () => import('@/components/view/SettingsTab.vue')
|
||||||
}],
|
}],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -36,6 +36,12 @@ export default {
|
|||||||
show: (record, route, user) => { return ['Running'].includes(record.state) && ['Admin'].includes(user.roletype) },
|
show: (record, route, user) => { return ['Running'].includes(record.state) && ['Admin'].includes(user.roletype) },
|
||||||
component: () => import('@views/infra/routers/RouterHealthCheck.vue')
|
component: () => import('@views/infra/routers/RouterHealthCheck.vue')
|
||||||
}],
|
}],
|
||||||
|
related: [{
|
||||||
|
name: 'vm',
|
||||||
|
title: 'label.instances',
|
||||||
|
param: 'networkid',
|
||||||
|
value: 'guestnetworkid'
|
||||||
|
}],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
api: 'startRouter',
|
api: 'startRouter',
|
||||||
@ -75,7 +81,7 @@ export default {
|
|||||||
api: 'listServiceOfferings',
|
api: 'listServiceOfferings',
|
||||||
params: (record) => {
|
params: (record) => {
|
||||||
return {
|
return {
|
||||||
virtualmachineid: record.virtualmachineid,
|
virtualmachineid: record.id,
|
||||||
issystem: true,
|
issystem: true,
|
||||||
systemvmtype: 'domainrouter'
|
systemvmtype: 'domainrouter'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,14 +68,20 @@ export default {
|
|||||||
icon: 'edit',
|
icon: 'edit',
|
||||||
label: 'label.edit',
|
label: 'label.edit',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['name', 'displaytext', 'guestvmcidr']
|
args: (record) => {
|
||||||
|
var fields = ['name', 'displaytext', 'guestvmcidr']
|
||||||
|
if (record.type === 'Isolated') {
|
||||||
|
fields.push(...['networkofferingid', 'networkdomain'])
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'restartNetwork',
|
api: 'restartNetwork',
|
||||||
icon: 'sync',
|
icon: 'sync',
|
||||||
label: 'label.restart.network',
|
label: 'label.restart.network',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['cleanup']
|
args: ['cleanup', 'makeredundant']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'replaceNetworkACLList',
|
api: 'replaceNetworkACLList',
|
||||||
@ -139,7 +145,8 @@ export default {
|
|||||||
label: 'label.add.vpc',
|
label: 'label.add.vpc',
|
||||||
docHelp: 'adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud',
|
docHelp: 'adminguide/networking_and_traffic.html#adding-a-virtual-private-cloud',
|
||||||
listView: true,
|
listView: true,
|
||||||
args: ['name', 'displaytext', 'zoneid', 'cidr', 'networkdomain', 'vpcofferingid', 'start']
|
popup: true,
|
||||||
|
component: () => import('@/views/network/CreateVpc.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'updateVPC',
|
api: 'updateVPC',
|
||||||
@ -154,7 +161,13 @@ export default {
|
|||||||
label: 'label.restart.vpc',
|
label: 'label.restart.vpc',
|
||||||
message: 'message.restart.vpc',
|
message: 'message.restart.vpc',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
args: ['makeredundant', 'cleanup']
|
args: (record) => {
|
||||||
|
var fields = ['cleanup']
|
||||||
|
if (!record.redundantvpcrouter) {
|
||||||
|
fields.push('makeredundant')
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'deleteVPC',
|
api: 'deleteVPC',
|
||||||
|
|||||||
@ -111,7 +111,7 @@ export default {
|
|||||||
message: 'message.detach.disk',
|
message: 'message.detach.disk',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => {
|
show: (record) => {
|
||||||
return record.type !== 'ROOT' && 'virtualmachineid' in record && record.virtualmachineid &&
|
return record.type !== 'ROOT' && record.virtualmachineid &&
|
||||||
['Running', 'Stopped', 'Destroyed'].includes(record.vmstate)
|
['Running', 'Stopped', 'Destroyed'].includes(record.vmstate)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -121,7 +121,11 @@ export default {
|
|||||||
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
|
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
|
||||||
label: 'label.action.take.snapshot',
|
label: 'label.action.take.snapshot',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'Ready' },
|
show: (record, store) => {
|
||||||
|
return record.state === 'Ready' && (record.hypervisor !== 'KVM' ||
|
||||||
|
record.hypervisor === 'KVM' && record.vmstate === 'Running' && store.features.kvmsnapshotenabled ||
|
||||||
|
record.hypervisor === 'KVM' && record.vmstate !== 'Running')
|
||||||
|
},
|
||||||
popup: true,
|
popup: true,
|
||||||
component: () => import('@/views/storage/TakeSnapshot.vue')
|
component: () => import('@/views/storage/TakeSnapshot.vue')
|
||||||
},
|
},
|
||||||
@ -131,7 +135,11 @@ export default {
|
|||||||
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
|
docHelp: 'adminguide/storage.html#working-with-volume-snapshots',
|
||||||
label: 'label.action.recurring.snapshot',
|
label: 'label.action.recurring.snapshot',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'Ready' },
|
show: (record, store) => {
|
||||||
|
return record.state === 'Ready' && (record.hypervisor !== 'KVM' ||
|
||||||
|
record.hypervisor === 'KVM' && record.vmstate === 'Running' && store.features.kvmsnapshotenabled ||
|
||||||
|
record.hypervisor === 'KVM' && record.vmstate !== 'Running')
|
||||||
|
},
|
||||||
popup: true,
|
popup: true,
|
||||||
component: () => import('@/views/storage/RecurringSnapshotVolume.vue'),
|
component: () => import('@/views/storage/RecurringSnapshotVolume.vue'),
|
||||||
mapping: {
|
mapping: {
|
||||||
@ -160,7 +168,7 @@ export default {
|
|||||||
label: 'label.migrate.volume',
|
label: 'label.migrate.volume',
|
||||||
args: ['volumeid', 'storageid', 'livemigrate'],
|
args: ['volumeid', 'storageid', 'livemigrate'],
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record, store) => { return record && record.state === 'Ready' && ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) },
|
show: (record, store) => { return record.state === 'Ready' && ['Admin'].includes(store.userInfo.roletype) && record.virtualmachineid },
|
||||||
popup: true,
|
popup: true,
|
||||||
component: () => import('@/views/storage/MigrateVolume.vue')
|
component: () => import('@/views/storage/MigrateVolume.vue')
|
||||||
},
|
},
|
||||||
@ -170,7 +178,7 @@ export default {
|
|||||||
label: 'label.action.download.volume',
|
label: 'label.action.download.volume',
|
||||||
message: 'message.download.volume.confirm',
|
message: 'message.download.volume.confirm',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record && record.state === 'Ready' && (record.vmstate === 'Stopped' || record.virtualmachineid == null) },
|
show: (record) => { return record.state === 'Ready' && (record.vmstate === 'Stopped' || !record.virtualmachineid) },
|
||||||
args: ['zoneid', 'mode'],
|
args: ['zoneid', 'mode'],
|
||||||
mapping: {
|
mapping: {
|
||||||
zoneid: {
|
zoneid: {
|
||||||
@ -187,7 +195,11 @@ export default {
|
|||||||
icon: 'picture',
|
icon: 'picture',
|
||||||
label: 'label.action.create.template.from.volume',
|
label: 'label.action.create.template.from.volume',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return (record.type === 'ROOT' && record.vmstate === 'Stopped') || (record.type !== 'ROOT' && !('virtualmachineid' in record) && !['Allocated', 'Uploaded', 'Destroy'].includes(record.state)) },
|
show: (record) => {
|
||||||
|
return !['Destroy', 'Destroyed', 'Expunging', 'Expunged', 'Migrating', 'Uploading', 'UploadError', 'Creating'].includes(record.state) &&
|
||||||
|
((record.type === 'ROOT' && record.vmstate === 'Stopped') ||
|
||||||
|
(record.type !== 'ROOT' && !record.virtualmachineid && !['Allocated', 'Uploaded'].includes(record.state)))
|
||||||
|
},
|
||||||
args: ['volumeid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
|
args: ['volumeid', 'name', 'displaytext', 'ostypeid', 'ispublic', 'isfeatured', 'isdynamicallyscalable', 'requireshvm', 'passwordenabled', 'sshkeyenabled'],
|
||||||
mapping: {
|
mapping: {
|
||||||
volumeid: {
|
volumeid: {
|
||||||
@ -214,6 +226,7 @@ export default {
|
|||||||
groupAction: true,
|
groupAction: true,
|
||||||
show: (record, store) => {
|
show: (record, store) => {
|
||||||
return ['Expunging', 'Expunged', 'UploadError'].includes(record.state) ||
|
return ['Expunging', 'Expunged', 'UploadError'].includes(record.state) ||
|
||||||
|
['Allocated', 'Uploaded'].includes(record.state) && record.type !== 'ROOT' && !record.virtualmachineid ||
|
||||||
((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy')
|
((['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) || store.features.allowuserexpungerecovervolume) && record.state === 'Destroy')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -227,7 +240,8 @@ export default {
|
|||||||
return (!['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !store.features.allowuserexpungerecovervolumestore) ? [] : ['expunge']
|
return (!['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !store.features.allowuserexpungerecovervolumestore) ? [] : ['expunge']
|
||||||
},
|
},
|
||||||
show: (record, store) => {
|
show: (record, store) => {
|
||||||
return (!['Creating'].includes(record.state) && record.type !== 'ROOT' && !('virtualmachineid' in record) && record.state !== 'Destroy')
|
return !['Destroy', 'Destroyed', 'Expunging', 'Expunged', 'Migrating', 'Uploading', 'UploadError', 'Creating', 'Allocated', 'Uploaded'].includes(record.state) &&
|
||||||
|
record.type !== 'ROOT' && !record.virtualmachineid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -62,7 +62,11 @@ export default {
|
|||||||
label: 'label.action.enable.user',
|
label: 'label.action.enable.user',
|
||||||
message: 'message.enable.user',
|
message: 'message.enable.user',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'disabled' }
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1) &&
|
||||||
|
record.state === 'disabled'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'disableUser',
|
api: 'disableUser',
|
||||||
@ -70,7 +74,11 @@ export default {
|
|||||||
label: 'label.action.disable.user',
|
label: 'label.action.disable.user',
|
||||||
message: 'message.disable.user',
|
message: 'message.disable.user',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
show: (record) => { return record.state === 'enabled' }
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1) &&
|
||||||
|
record.state === 'enabled'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: 'authorizeSamlSso',
|
api: 'authorizeSamlSso',
|
||||||
@ -78,6 +86,9 @@ export default {
|
|||||||
label: 'Configure SAML SSO Authorization',
|
label: 'Configure SAML SSO Authorization',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype)
|
||||||
|
},
|
||||||
component: () => import('@/views/iam/ConfigureSamlSsoAuth.vue')
|
component: () => import('@/views/iam/ConfigureSamlSsoAuth.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -85,7 +96,11 @@ export default {
|
|||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
label: 'label.action.delete.user',
|
label: 'label.action.delete.user',
|
||||||
message: 'message.delete.user',
|
message: 'message.delete.user',
|
||||||
dataView: true
|
dataView: true,
|
||||||
|
show: (record, store) => {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(store.userInfo.roletype) && !record.isdefault &&
|
||||||
|
!(record.domain === 'ROOT' && record.account === 'admin' && record.accounttype === 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2490,6 +2490,7 @@
|
|||||||
"message.attach.volume": "Please fill in the following data to attach a new volume. If you are attaching a disk volume to a Windows based virtual machine, you will need to reboot the instance to see the attached disk.",
|
"message.attach.volume": "Please fill in the following data to attach a new volume. If you are attaching a disk volume to a Windows based virtual machine, you will need to reboot the instance to see the attached disk.",
|
||||||
"message.authorization.failed": "Session expired, authorization verification failed",
|
"message.authorization.failed": "Session expired, authorization verification failed",
|
||||||
"message.backup.attach.restore": "Please confirm that you want to restore and attach the volume from the backup?",
|
"message.backup.attach.restore": "Please confirm that you want to restore and attach the volume from the backup?",
|
||||||
|
"message.backup.create": "Are you sure you want create a VM backup?",
|
||||||
"message.backup.offering.remove": "Are you sure you want to remove VM from backup offering and delete the backup chain?",
|
"message.backup.offering.remove": "Are you sure you want to remove VM from backup offering and delete the backup chain?",
|
||||||
"message.backup.restore": "Please confirm that you want to restore the vm backup?",
|
"message.backup.restore": "Please confirm that you want to restore the vm backup?",
|
||||||
"message.basic.mode.desc": "Choose this network model if you do <b>*<u>not</u>*</b> want to enable any VLAN support. All virtual instances created under this network model will be assigned an IP directly from the network and security groups are used to provide security and segregation.",
|
"message.basic.mode.desc": "Choose this network model if you do <b>*<u>not</u>*</b> want to enable any VLAN support. All virtual instances created under this network model will be assigned an IP directly from the network and security groups are used to provide security and segregation.",
|
||||||
|
|||||||
@ -238,6 +238,7 @@
|
|||||||
</span>
|
</span>
|
||||||
<span v-else-if="field.type==='long'">
|
<span v-else-if="field.type==='long'">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
|
style="width: 100%;"
|
||||||
v-decorator="[field.name, {
|
v-decorator="[field.name, {
|
||||||
rules: [{ required: field.required, message: `${$t('message.validate.number')}` }]
|
rules: [{ required: field.required, message: `${$t('message.validate.number')}` }]
|
||||||
}]"
|
}]"
|
||||||
@ -747,16 +748,21 @@ export default {
|
|||||||
if (res === 'count') {
|
if (res === 'count') {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
const filter = this.currentAction.mapping[param.name].filter
|
||||||
|
if (filter) {
|
||||||
|
param.opts = json[obj][res].filter(filter)
|
||||||
|
} else {
|
||||||
param.opts = json[obj][res]
|
param.opts = json[obj][res]
|
||||||
|
}
|
||||||
if (['listTemplates', 'listIsos'].includes(possibleApi)) {
|
if (['listTemplates', 'listIsos'].includes(possibleApi)) {
|
||||||
param.opts = [...new Map(param.opts.map(x => [x.id, x])).values()]
|
param.opts = [...new Map(param.opts.map(x => [x.id, x])).values()]
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
this.$forceUpdate()
|
this.$forceUpdate()
|
||||||
break
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.log(error.stack)
|
console.log(error.stack)
|
||||||
param.loading = false
|
param.loading = false
|
||||||
|
|||||||
167
ui/src/views/compute/AttachIso.vue
Normal file
167
ui/src/views/compute/AttachIso.vue
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
// 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-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item :label="$t('label.iso.name')">
|
||||||
|
<a-select
|
||||||
|
:loading="loading"
|
||||||
|
v-decorator="['id', {
|
||||||
|
initialValue: this.selectedIso,
|
||||||
|
rules: [{ required: true, message: `${this.$t('label.required')}`}]
|
||||||
|
}]" >
|
||||||
|
<a-select-option v-for="iso in isos" :key="iso.id">
|
||||||
|
{{ iso.displaytext || iso.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AttachIso',
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['parentFetchData'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
selectedIso: '',
|
||||||
|
isos: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this)
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData () {
|
||||||
|
const isoFiters = ['featured', 'community', 'selfexecutable']
|
||||||
|
this.loading = true
|
||||||
|
const promises = []
|
||||||
|
isoFiters.forEach((filter) => {
|
||||||
|
promises.push(this.fetchIsos(filter))
|
||||||
|
})
|
||||||
|
Promise.all(promises).then(() => {
|
||||||
|
this.isos = _.uniqBy(this.isos, 'id')
|
||||||
|
if (this.isos.length > 0) {
|
||||||
|
this.selectedIso = this.isos[0].id
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchIsos (isoFilter) {
|
||||||
|
const params = {
|
||||||
|
listall: true,
|
||||||
|
zoneid: this.resource.zoneid,
|
||||||
|
isready: true,
|
||||||
|
isofilter: isoFilter
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listIsos', params).then((response) => {
|
||||||
|
const isos = response.listisosresponse.iso || []
|
||||||
|
this.isos.push(...isos)
|
||||||
|
resolve(response)
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
id: values.id,
|
||||||
|
virtualmachineid: this.resource.id
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
const title = this.$t('label.action.attach.iso')
|
||||||
|
api('attachIso', params).then(json => {
|
||||||
|
const jobId = json.attachisoresponse.jobid
|
||||||
|
if (jobId) {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId,
|
||||||
|
successMethod: result => {
|
||||||
|
this.$store.dispatch('AddAsyncJob', {
|
||||||
|
title: title,
|
||||||
|
jobid: jobId,
|
||||||
|
status: this.$t('progress')
|
||||||
|
})
|
||||||
|
this.parentFetchData()
|
||||||
|
},
|
||||||
|
successMessage: `${this.$t('label.action.attach.iso')} ${this.$t('label.success')}`,
|
||||||
|
loadingMessage: `${title} ${this.$t('label.in.progress')}`,
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
text-align: right;
|
||||||
|
button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -132,6 +132,13 @@
|
|||||||
:routerlinks="(record) => { return { id: '/backup/' + record.id } }"
|
:routerlinks="(record) => { return { id: '/backup/' + record.id } }"
|
||||||
:showSearch="false"/>
|
:showSearch="false"/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
<a-tab-pane :tab="$t('label.securitygroups')" key="securitygroups" v-if="this.resource.securitygroup && this.resource.securitygroup.length > 0">
|
||||||
|
<ListResourceTable
|
||||||
|
:items="this.resource.securitygroup"
|
||||||
|
:columns="['name', 'description']"
|
||||||
|
:routerlinks="(record) => { return { name: '/securitygroups/' + record.id } }"
|
||||||
|
:showSearch="false"/>
|
||||||
|
</a-tab-pane>
|
||||||
<a-tab-pane :tab="$t('label.settings')" key="settings">
|
<a-tab-pane :tab="$t('label.settings')" key="settings">
|
||||||
<DetailSettings :resource="resource" :loading="loading" />
|
<DetailSettings :resource="resource" :loading="loading" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
<div class="capacity-dashboard-button">
|
<div class="capacity-dashboard-button">
|
||||||
<a-button
|
<a-button
|
||||||
shape="round"
|
shape="round"
|
||||||
@click="listCapacity(zoneSelected, true)">
|
@click="() => { listCapacity(zoneSelected, true); listEvents() }">
|
||||||
{{ $t('label.fetch.latest') }}
|
{{ $t('label.fetch.latest') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -243,13 +243,17 @@ export default {
|
|||||||
this.form = this.$form.createForm(this)
|
this.form = this.$form.createForm(this)
|
||||||
this.apiConfig = this.$store.getters.apis.createAccount || {}
|
this.apiConfig = this.$store.getters.apis.createAccount || {}
|
||||||
this.apiParams = {}
|
this.apiParams = {}
|
||||||
|
if (this.apiConfig.params) {
|
||||||
this.apiConfig.params.forEach(param => {
|
this.apiConfig.params.forEach(param => {
|
||||||
this.apiParams[param.name] = param
|
this.apiParams[param.name] = param
|
||||||
})
|
})
|
||||||
|
}
|
||||||
this.apiConfig = this.$store.getters.apis.authorizeSamlSso || {}
|
this.apiConfig = this.$store.getters.apis.authorizeSamlSso || {}
|
||||||
|
if (this.apiConfig.params) {
|
||||||
this.apiConfig.params.forEach(param => {
|
this.apiConfig.params.forEach(param => {
|
||||||
this.apiParams[param.name] = param
|
this.apiParams[param.name] = param
|
||||||
})
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.fetchData()
|
this.fetchData()
|
||||||
|
|||||||
@ -96,7 +96,7 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
roles: [],
|
roles: [],
|
||||||
defaultRoles: ['Admin', 'DomainAdmin', 'ResourceAdmin', 'User'],
|
defaultRoles: ['Admin', 'DomainAdmin', 'User'],
|
||||||
createRoleUsing: 'type',
|
createRoleUsing: 'type',
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,13 +210,19 @@ export default {
|
|||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
this.action.paramFields = []
|
this.action.paramFields = []
|
||||||
if (action.args && action.args.length > 0) {
|
if (action.args) {
|
||||||
this.action.paramFields = action.args.map(function (arg) {
|
var args = action.args
|
||||||
|
if (typeof action.args === 'function') {
|
||||||
|
args = action.args(action.resource, this.$store.getters)
|
||||||
|
}
|
||||||
|
if (args.length > 0) {
|
||||||
|
this.action.paramFields = args.map(function (arg) {
|
||||||
return paramFields.filter(function (param) {
|
return paramFields.filter(function (param) {
|
||||||
return param.name.toLowerCase() === arg.toLowerCase()
|
return param.name.toLowerCase() === arg.toLowerCase()
|
||||||
})[0]
|
})[0]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.showAction = true
|
this.showAction = true
|
||||||
for (const param of this.action.paramFields) {
|
for (const param of this.action.paramFields) {
|
||||||
if (param.type === 'list' && ['tags', 'hosttags'].includes(param.name)) {
|
if (param.type === 'list' && ['tags', 'hosttags'].includes(param.name)) {
|
||||||
|
|||||||
@ -113,9 +113,16 @@
|
|||||||
:help="zoneErrorMessage">
|
:help="zoneErrorMessage">
|
||||||
<a-select
|
<a-select
|
||||||
v-decorator="['zoneid', {
|
v-decorator="['zoneid', {
|
||||||
initialValue: this.zoneSelected
|
initialValue: this.zoneSelected,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `${this.$t('message.error.select')}`
|
||||||
|
}
|
||||||
|
]
|
||||||
}]"
|
}]"
|
||||||
@change="handlerSelectZone"
|
@change="handlerSelectZone"
|
||||||
|
:placeholder="apiParams.zoneid.description"
|
||||||
:loading="zones.loading">
|
:loading="zones.loading">
|
||||||
<a-select-option :value="zone.id" v-for="zone in zones.opts" :key="zone.id">
|
<a-select-option :value="zone.id" v-for="zone in zones.opts" :key="zone.id">
|
||||||
<div v-if="zone.name !== $t('label.all.zone')">
|
<div v-if="zone.name !== $t('label.all.zone')">
|
||||||
|
|||||||
253
ui/src/views/network/CreateVpc.vue
Normal file
253
ui/src/views/network/CreateVpc.vue
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
// 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-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:form="form"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.name') }}
|
||||||
|
<a-tooltip :title="apiParams.name.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['name', {
|
||||||
|
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||||
|
}]"
|
||||||
|
:placeholder="apiParams.name.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.displaytext') }}
|
||||||
|
<a-tooltip :title="apiParams.displaytext.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['displaytext', {
|
||||||
|
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||||
|
}]"
|
||||||
|
:placeholder="apiParams.displaytext.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.zoneid') }}
|
||||||
|
<a-tooltip :title="apiParams.zoneid.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
:loading="loadingZone"
|
||||||
|
v-decorator="['zoneid', {
|
||||||
|
initialValue: this.selectedZone,
|
||||||
|
rules: [{ required: true, message: `${this.$t('label.required')}`}]
|
||||||
|
}]"
|
||||||
|
@change="val => changeZone(val)">
|
||||||
|
<a-select-option v-for="zone in zones" :key="zone.id">
|
||||||
|
{{ zone.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.cidr') }}
|
||||||
|
<a-tooltip :title="apiParams.cidr.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['cidr', {
|
||||||
|
rules: [{ required: true, message: $t('message.error.required.input') }]
|
||||||
|
}]"
|
||||||
|
:placeholder="apiParams.cidr.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.networkdomain') }}
|
||||||
|
<a-tooltip :title="apiParams.networkdomain.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-input
|
||||||
|
v-decorator="['networkdomain']"
|
||||||
|
:placeholder="apiParams.networkdomain.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.vpcofferingid') }}
|
||||||
|
<a-tooltip :title="apiParams.vpcofferingid.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-select
|
||||||
|
:loading="loadingOffering"
|
||||||
|
v-decorator="['vpcofferingid', {
|
||||||
|
initialValue: this.selectedOffering,
|
||||||
|
rules: [{ required: true, message: `${this.$t('label.required')}`}]}]">
|
||||||
|
<a-select-option :value="offering.id" v-for="offering in vpcOfferings" :key="offering.id">
|
||||||
|
{{ offering.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<span slot="label">
|
||||||
|
{{ $t('label.start') }}
|
||||||
|
<a-tooltip :title="apiParams.start.description">
|
||||||
|
<a-icon type="info-circle" style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
<a-switch v-decorator="['start']" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ this.$t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
export default {
|
||||||
|
name: 'CreateVpc',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
loadingZone: false,
|
||||||
|
loadingOffering: false,
|
||||||
|
selectedZone: '',
|
||||||
|
zones: [],
|
||||||
|
vpcOfferings: [],
|
||||||
|
selectedOffering: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.form = this.$form.createForm(this)
|
||||||
|
this.apiParams = {}
|
||||||
|
var apiConfig = this.$store.getters.apis.createVPC || []
|
||||||
|
apiConfig.params.forEach(param => {
|
||||||
|
this.apiParams[param.name] = param
|
||||||
|
})
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData () {
|
||||||
|
this.fetchZones()
|
||||||
|
},
|
||||||
|
fetchZones () {
|
||||||
|
this.loadingZone = true
|
||||||
|
api('listZones', { listAll: true }).then((response) => {
|
||||||
|
const listZones = response.listzonesresponse.zone || []
|
||||||
|
this.zones = listZones.filter(zone => !zone.securitygroupsenabled)
|
||||||
|
this.selectedZone = ''
|
||||||
|
if (this.zones.length > 0) {
|
||||||
|
this.selectedZone = this.zones[0].id
|
||||||
|
this.changeZone(this.selectedZone)
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingZone = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changeZone (value) {
|
||||||
|
this.selectedZone = value
|
||||||
|
if (this.selectedZone === '') {
|
||||||
|
this.selectedOffering = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.fetchOfferings()
|
||||||
|
},
|
||||||
|
fetchOfferings () {
|
||||||
|
this.loadingOffering = true
|
||||||
|
api('listVPCOfferings', { zoneid: this.selectedZone, state: 'Enabled' }).then((reponse) => {
|
||||||
|
this.vpcOfferings = reponse.listvpcofferingsresponse.vpcoffering
|
||||||
|
this.selectedOffering = this.vpcOfferings[0].id || ''
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadingOffering = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
this.form.validateFields((err, values) => {
|
||||||
|
if (err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {}
|
||||||
|
for (const key in values) {
|
||||||
|
const input = values[key]
|
||||||
|
if (input === '' || input === null || input === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
params[key] = input
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
const title = this.$t('label.add.vpc')
|
||||||
|
const description = this.$t('message.success.add.vpc.network')
|
||||||
|
api('createVPC', params).then(json => {
|
||||||
|
const jobId = json.createvpcresponse.jobid
|
||||||
|
if (jobId) {
|
||||||
|
this.$pollJob({
|
||||||
|
jobId,
|
||||||
|
successMethod: result => {
|
||||||
|
this.$store.dispatch('AddAsyncJob', {
|
||||||
|
title: title,
|
||||||
|
jobid: jobId,
|
||||||
|
description: description,
|
||||||
|
status: this.$t('progress')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadingMessage: `${title} ${this.$t('label.in.progress')}`,
|
||||||
|
catchMessage: this.$t('error.fetching.async.job.result')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.closeAction()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
text-align: right;
|
||||||
|
button {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -259,7 +259,7 @@
|
|||||||
</a-spin>
|
</a-spin>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane :tab="$t('label.virtual.routers')" key="vr" v-if="'listRouters' in $store.getters.apis">
|
<a-tab-pane :tab="$t('label.virtual.routers')" key="vr" v-if="$store.getters.userInfo.roletype === 'Admin'">
|
||||||
<RoutersTab :resource="resource" :loading="loading" />
|
<RoutersTab :resource="resource" :loading="loading" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
|
|||||||
@ -177,7 +177,7 @@ export default {
|
|||||||
params[key] = input
|
params[key] = input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.allowuserdrivenbackups = values.allowuserdrivenbackups ? values.allowuserdrivenbackups : false
|
params.allowuserdrivenbackups = values.allowuserdrivenbackups ? values.allowuserdrivenbackups : true
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const title = this.$t('label.import.offering')
|
const title = this.$t('label.import.offering')
|
||||||
api('importBackupOffering', params).then(json => {
|
api('importBackupOffering', params).then(json => {
|
||||||
|
|||||||
@ -36,21 +36,22 @@ export default {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.resource = newItem
|
this.resource = newItem
|
||||||
this.fetchProjectAccounts()
|
this.determineOwner()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchProjectAccounts () {
|
determineOwner () {
|
||||||
var owner = this.resource.owner
|
var owner = this.resource.owner || []
|
||||||
|
// If current backend does not support multiple project admins
|
||||||
|
if (owner.length === 0) {
|
||||||
|
this.$set(this.resource, 'isCurrentUserProjectAdmin', this.resource.account === this.$store.getters.userInfo.account)
|
||||||
|
return
|
||||||
|
}
|
||||||
owner = owner.filter(projectaccount => {
|
owner = owner.filter(projectaccount => {
|
||||||
return (projectaccount.userid && projectaccount.userid === this.$store.getters.userInfo.id) ||
|
return (projectaccount.userid && projectaccount.userid === this.$store.getters.userInfo.id) ||
|
||||||
projectaccount.account === this.$store.getters.userInfo.account
|
projectaccount.account === this.$store.getters.userInfo.account
|
||||||
})
|
})
|
||||||
var isCurrentUserProjectAdmin = false
|
this.$set(this.resource, 'isCurrentUserProjectAdmin', owner.length > 0)
|
||||||
if (owner.length > 0) {
|
|
||||||
isCurrentUserProjectAdmin = true
|
|
||||||
}
|
|
||||||
this.$set(this.resource, 'isCurrentUserProjectAdmin', isCurrentUserProjectAdmin)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
compact>
|
compact>
|
||||||
<a-input ref="input" :value="inputKey" @change="handleKeyChange" style="width: 100px; text-align: center" :placeholder="$t('label.key')" />
|
<a-input ref="input" :value="inputKey" @change="handleKeyChange" style="width: 100px; text-align: center" :placeholder="$t('label.key')" />
|
||||||
<a-input style=" width: 30px; border-left: 0; pointer-events: none; backgroundColor: #fff" placeholder="=" disabled />
|
<a-input style=" width: 30px; border-left: 0; pointer-events: none; backgroundColor: #fff" placeholder="=" disabled />
|
||||||
<a-input :value="inputValue" @change="handleValueChange" style="width: 100px; text-align: center; border-left: 0" :placeholder="$('label.value')" />
|
<a-input :value="inputValue" @change="handleValueChange" style="width: 100px; text-align: center; border-left: 0" :placeholder="$t('label.value')" />
|
||||||
<a-button shape="circle" size="small" @click="handleInputConfirm">
|
<a-button shape="circle" size="small" @click="handleInputConfirm">
|
||||||
<a-icon type="check"/>
|
<a-icon type="check"/>
|
||||||
</a-button>
|
</a-button>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user