Merge release branch 4.19 to main

* 4.19:
  engine-schema: add removed columne for cloud.user_data, fix delete op (#9120)
  ui: fix create menu item access (#9104)
  server: honor listall param for listiso api (#9064)
  ui: add move to top/bottom for acl rules list (#9119)
  ui: fix columns for exportacls csv (#9118)
  ui: fix create network access in deploy vm wizard (#9117)
  UI: Add search filters (#9068)
This commit is contained in:
Daan Hoogland 2024-05-28 09:44:05 +02:00
commit f1c3d2c4be
25 changed files with 374 additions and 15 deletions

View File

@ -24,8 +24,12 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
import java.util.UUID;
import com.cloud.utils.db.GenericDao;
@Entity
@Table(name = "user_data")
public class UserDataVO implements UserData {
@ -58,6 +62,9 @@ public class UserDataVO implements UserData {
@Column(name = "params", length = 4096)
private String params;
@Column(name = GenericDao.REMOVED_COLUMN)
private Date removed;
@Override
public long getDomainId() {
return domainId;
@ -117,4 +124,12 @@ public class UserDataVO implements UserData {
public void setParams(String params) {
this.params = params;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
public Date getRemoved() {
return removed;
}
}

View File

@ -63,3 +63,5 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`usage_vpc` (
) ENGINE=InnoDB CHARSET=utf8;
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.cloud_usage', 'state', 'VARCHAR(100) DEFAULT NULL');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data', 'removed', 'datetime COMMENT "date removed or null, if still present"');

View File

@ -4799,7 +4799,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
boolean showRemovedISO = cmd.getShowRemoved();
Account caller = CallContext.current().getCallingAccount();
boolean listAll = false;
boolean listAll = cmd.listAll();
if (isoFilter != null && isoFilter == TemplateFilter.all) {
if (caller.getType() == Account.Type.NORMAL) {
throw new InvalidParameterValueException("Filter " + TemplateFilter.all + " can be specified by admin only");

View File

@ -709,6 +709,7 @@
"label.destnetworkuuid": "Network",
"label.destport": "Destination Ports",
"label.destroy": "Destroy",
"label.destroying": "Destroying",
"label.destroyed": "Destroyed",
"label.destroy.router": "Destroy router",
"label.deststartport": "Destination Start Port",
@ -934,6 +935,7 @@
"label.fwdeviceid": "ID",
"label.fwdevicestate": "Status",
"label.gateway": "Gateway",
"label.global": "Global",
"label.global.settings": "Global Settings",
"label.globo.dns": "GloboDNS",
"label.globo.dns.configuration": "GloboDNS configuration",
@ -1347,6 +1349,7 @@
"label.min_balance": "Min balance",
"label.mincpunumber": "Min CPU cores",
"label.minimum": "Minimum",
"label.minimumsemanticversion": "Minimum semantic version",
"label.miniops": "Min IOPS",
"label.minmaxiops": "Min IOPS / Max IOPS",
"label.minmembers": "Min members",
@ -1701,6 +1704,7 @@
"label.reboot": "Reboot",
"label.receivedbytes": "Bytes received",
"label.recover.vm": "Recover Instance",
"label.recovering": "Recovering",
"label.redirect": "Redirect to:",
"label.redirecturi": "Redirect URI",
"label.redundantrouter": "Redundant router",
@ -1843,6 +1847,7 @@
"label.scaledown.policy": "ScaleDown policy",
"label.scaleup.policies": "ScaleUp policies",
"label.scaleup.policy": "ScaleUp policy",
"label.scaling": "Scaling",
"label.schedule": "Schedule",
"label.schedule.add": "Add schedule",
"label.scheduled.backups": "Scheduled backups",
@ -2211,6 +2216,7 @@
"label.update.vmware.datacenter": "Update VMWare datacenter",
"label.updating": "Updating",
"label.upgrade.router.newer.template": "Upgrade router to use newer Template",
"label.upgrading": "Upgrading",
"label.upload": "Upload",
"label.upload.description": "Path to upload objects at",
"label.upload.path": "Upload path",
@ -3058,7 +3064,8 @@
"message.network.offering.promiscuous.mode": "Applicable for guest Networks on VMware hypervisor only.\nReject - The switch drops any outbound frame from a virtual machine adapter with a source MAC address that is different from the one in the .vmx configuration file.\nAccept - The switch does not perform filtering, and permits all outbound frames.\nNone - Default to value from global setting.",
"message.network.removenic": "Please confirm that want to remove this NIC, which will also remove the associated Network from the Instance.",
"message.network.secondaryip": "Please confirm that you would like to acquire a new secondary IP for this NIC. \n NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.",
"message.network.selection": "Choose one or more Networks to attach the Instance to. A new Network can also be created here.",
"message.network.selection": "Choose one or more Networks to attach the Instance to.",
"message.network.selection.new.network": "A new Network can also be created here.",
"message.network.updateip": "Please confirm that you would like to change the IP address for this NIC on the Instance.",
"message.network.usage.info.data.points": "Each data point represents the difference in data traffic since the last data point.",
"message.network.usage.info.sum.of.vnics": "The Network usage shown is made up of the sum of data traffic from all the vNICs in the Instance.",

View File

@ -19,7 +19,7 @@
<a-dropdown>
<template #overlay>
<a-menu>
<a-menu-item style="width: 100%; padding: 12px">
<a-menu-item style="width: 100%; padding: 12px" v-if="'deployVirtualMachine' in $store.getters.apis">
<router-link :to="{ path: '/action/deployVirtualMachine'}">
<a-row>
<a-col style="margin-right: 12px">
@ -38,7 +38,7 @@
</a-row>
</router-link>
</a-menu-item>
<a-menu-item style="width: 100%; padding: 12px" v-if="'listKubernetesClusters' in $store.getters.apis">
<a-menu-item style="width: 100%; padding: 12px" v-if="'createKubernetesCluster' in $store.getters.apis">
<router-link :to="{ path: '/kubernetes', query: { action: 'createKubernetesCluster' } }">
<a-row>
<a-col style="margin-right: 12px">
@ -57,7 +57,7 @@
</a-row>
</router-link>
</a-menu-item>
<a-menu-item style="width: 100%; padding: 12px">
<a-menu-item style="width: 100%; padding: 12px" v-if="'createVolume' in $store.getters.apis">
<router-link :to="{ path: '/volume', query: { action: 'createVolume' } }">
<a-row>
<a-col style="margin-right: 12px">
@ -76,7 +76,7 @@
</a-row>
</router-link>
</a-menu-item>
<a-menu-item style="width: 100%; padding: 12px">
<a-menu-item style="width: 100%; padding: 12px" v-if="'createNetwork' in $store.getters.apis">
<router-link :to="{ path: '/guestnetwork', query: { action: 'createNetwork' } }">
<a-row>
<a-col style="margin-right: 12px">
@ -95,7 +95,7 @@
</a-row>
</router-link>
</a-menu-item>
<a-menu-item style="width: 100%; padding: 12px">
<a-menu-item style="width: 100%; padding: 12px" v-if="'createVPC' in $store.getters.apis">
<router-link :to="{ path: '/vpc', query: { action: 'createVPC' } }">
<a-row>
<a-col style="margin-right: 12px">
@ -114,7 +114,7 @@
</a-row>
</router-link>
</a-menu-item>
<a-menu-item style="width: 100%; padding: 12px">
<a-menu-item style="width: 100%; padding: 12px" v-if="'registerTemplate' in $store.getters.apis">
<router-link :to="{ path: '/template', query: { action: 'registerTemplate' } }">
<a-row>
<a-col style="margin-right: 12px">

View File

@ -79,7 +79,7 @@
</span>
<global-outlined v-else style="margin-right: 5px" />
</span>
<span v-if="(field.name.startsWith('domain'))">
<span v-if="(field.name.startsWith('domain') || field.name === 'account')">
<span v-if="opt.icon">
<resource-icon :image="opt.icon.base64image" size="1x" style="margin-right: 5px"/>
</span>
@ -268,6 +268,9 @@ export default {
if (item === 'domainid' && !('listDomains' in this.$store.getters.apis)) {
return true
}
if (item === 'account' && !('listAccounts' in this.$store.getters.apis)) {
return true
}
if (item === 'account' && !('addAccountToProject' in this.$store.getters.apis || 'createAccount' in this.$store.getters.apis)) {
return true
}
@ -280,7 +283,10 @@ export default {
if (item === 'groupid' && !('listInstanceGroups' in this.$store.getters.apis)) {
return true
}
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'level', 'clusterid', 'podid', 'groupid', 'entitytype', 'type'].includes(item)) {
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
'type'].includes(item)
) {
type = 'list'
} else if (item === 'tags') {
type = 'tag'
@ -305,6 +311,11 @@ export default {
this.fields[typeIndex].loading = true
this.fields[typeIndex].opts = this.fetchGuestNetworkTypes()
this.fields[typeIndex].loading = false
} else if (this.$route.path === '/role' || this.$route.path.includes('/role/')) {
const typeIndex = this.fields.findIndex(item => item.name === 'type')
this.fields[typeIndex].loading = true
this.fields[typeIndex].opts = this.fetchRoleTypes()
this.fields[typeIndex].loading = false
}
}
@ -329,6 +340,34 @@ export default {
this.fields[entityTypeIndex].loading = false
}
if (arrayField.includes('accounttype')) {
const accountTypeIndex = this.fields.findIndex(item => item.name === 'accounttype')
this.fields[accountTypeIndex].loading = true
this.fields[accountTypeIndex].opts = this.fetchAccountTypes()
this.fields[accountTypeIndex].loading = false
}
if (arrayField.includes('systemvmtype')) {
const systemVmTypeIndex = this.fields.findIndex(item => item.name === 'systemvmtype')
this.fields[systemVmTypeIndex].loading = true
this.fields[systemVmTypeIndex].opts = this.fetchSystemVmTypes()
this.fields[systemVmTypeIndex].loading = false
}
if (arrayField.includes('scope')) {
const scopeIndex = this.fields.findIndex(item => item.name === 'scope')
this.fields[scopeIndex].loading = true
this.fields[scopeIndex].opts = this.fetchStoragePoolScope()
this.fields[scopeIndex].loading = false
}
if (arrayField.includes('provider')) {
const providerIndex = this.fields.findIndex(item => item.name === 'provider')
this.fields[providerIndex].loading = true
this.fields[providerIndex].opts = this.fetchImageStoreProviders()
this.fields[providerIndex].loading = false
}
if (arrayField.includes('resourcetype')) {
const resourceTypeIndex = this.fields.findIndex(item => item.name === 'resourcetype')
this.fields[resourceTypeIndex].loading = true
@ -351,6 +390,8 @@ export default {
let typeIndex = -1
let zoneIndex = -1
let domainIndex = -1
let accountIndex = -1
let hypervisorIndex = -1
let imageStoreIndex = -1
let storageIndex = -1
let podIndex = -1
@ -362,6 +403,10 @@ export default {
typeIndex = this.fields.findIndex(item => item.name === 'type')
this.fields[typeIndex].loading = true
promises.push(await this.fetchAlertTypes())
} else if (this.$route.path === '/affinitygroup') {
typeIndex = this.fields.findIndex(item => item.name === 'type')
this.fields[typeIndex].loading = true
promises.push(await this.fetchAffinityGroupTypes())
}
}
@ -377,6 +422,18 @@ export default {
promises.push(await this.fetchDomains(searchKeyword))
}
if (arrayField.includes('account')) {
accountIndex = this.fields.findIndex(item => item.name === 'account')
this.fields[accountIndex].loading = true
promises.push(await this.fetchAccounts(searchKeyword))
}
if (arrayField.includes('hypervisor')) {
hypervisorIndex = this.fields.findIndex(item => item.name === 'hypervisor')
this.fields[hypervisorIndex].loading = true
promises.push(await this.fetchHypervisors())
}
if (arrayField.includes('imagestoreid')) {
imageStoreIndex = this.fields.findIndex(item => item.name === 'imagestoreid')
this.fields[imageStoreIndex].loading = true
@ -426,6 +483,18 @@ export default {
this.fields[domainIndex].opts = this.sortArray(domain[0].data, 'path')
}
}
if (accountIndex > -1) {
const account = response.filter(item => item.type === 'account')
if (account && account.length > 0) {
this.fields[accountIndex].opts = this.sortArray(account[0].data, 'name')
}
}
if (hypervisorIndex > -1) {
const hypervisor = response.filter(item => item.type === 'hypervisor')
if (hypervisor && hypervisor.length > 0) {
this.fields[hypervisorIndex].opts = this.sortArray(hypervisor[0].data, 'name')
}
}
if (imageStoreIndex > -1) {
const imageStore = response.filter(item => item.type === 'imagestoreid')
if (imageStore && imageStore.length > 0) {
@ -539,6 +608,32 @@ export default {
})
})
},
fetchAccounts (searchKeyword) {
return new Promise((resolve, reject) => {
api('listAccounts', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => {
const account = json.listaccountsresponse.account
resolve({
type: 'account',
data: account
})
}).catch(error => {
reject(error.response.headers['x-description'])
})
})
},
fetchHypervisors () {
return new Promise((resolve, reject) => {
api('listHypervisors').then(json => {
const hypervisor = json.listhypervisorsresponse.hypervisor.map(a => { return { id: a.name, name: a.name } })
resolve({
type: 'hypervisor',
data: hypervisor
})
}).catch(error => {
reject(error.response.headers['x-description'])
})
})
},
fetchImageStores (searchKeyword) {
return new Promise((resolve, reject) => {
api('listImageStores', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => {
@ -627,6 +722,41 @@ export default {
})
}
},
fetchAffinityGroupTypes () {
if (this.alertTypes.length > 0) {
return new Promise((resolve, reject) => {
resolve({
type: 'type',
data: this.alertTypes
})
})
} else {
return new Promise((resolve, reject) => {
api('listAffinityGroupTypes').then(json => {
const alerttypes = json.listaffinitygrouptypesresponse.affinityGroupType.map(a => {
let name = a.type
if (a.type === 'host anti-affinity') {
name = 'host anti-affinity (Strict)'
} else if (a.type === 'host affinity') {
name = 'host affinity (Strict)'
} else if (a.type === 'non-strict host anti-affinity') {
name = 'host anti-affinity (Non-Strict)'
} else if (a.type === 'non-strict host affinity') {
name = 'host affinity (Non-Strict)'
}
return { id: a.type, name: name }
})
this.alertTypes = alerttypes
resolve({
type: 'type',
data: alerttypes
})
}).catch(error => {
reject(error.response.headers['x-description'])
})
})
}
},
fetchGuestNetworkTypes () {
const types = []
if (this.apiName.indexOf('listNetworks') > -1) {
@ -645,6 +775,108 @@ export default {
}
return types
},
fetchAccountTypes () {
const types = []
if (this.apiName.indexOf('listAccounts') > -1) {
types.push({
id: '1',
name: 'Admin'
})
types.push({
id: '2',
name: 'DomainAdmin'
})
types.push({
id: '3',
name: 'User'
})
}
return types
},
fetchSystemVmTypes () {
const types = []
if (this.apiName.indexOf('listSystemVms') > -1) {
types.push({
id: 'consoleproxy',
name: 'label.console.proxy.vm'
})
types.push({
id: 'secondarystoragevm',
name: 'label.secondary.storage.vm'
})
}
return types
},
fetchStoragePoolScope () {
const types = []
if (this.apiName.indexOf('listStoragePools') > -1) {
types.push({
id: 'HOST',
name: 'label.hostname'
})
types.push({
id: 'CLUSTER',
name: 'label.cluster'
})
types.push({
id: 'ZONE',
name: 'label.zone'
})
types.push({
id: 'REGION',
name: 'label.region'
})
types.push({
id: 'GLOBAL',
name: 'label.global'
})
}
return types
},
fetchImageStoreProviders () {
const types = []
if (this.apiName.indexOf('listImageStores') > -1) {
types.push({
id: 'NFS',
name: 'NFS'
})
types.push({
id: 'SMB/CIFS',
name: 'SMB/CIFS'
})
types.push({
id: 'S3',
name: 'S3'
})
types.push({
id: 'Swift',
name: 'Swift'
})
}
return types
},
fetchRoleTypes () {
const types = []
if (this.apiName.indexOf('listRoles') > -1) {
types.push({
id: 'Admin',
name: 'Admin'
})
types.push({
id: 'ResourceAdmin',
name: 'ResourceAdmin'
})
types.push({
id: 'DomainAdmin',
name: 'DomainAdmin'
})
types.push({
id: 'User',
name: 'User'
})
}
return types
},
fetchState () {
if (this.apiName.includes('listVolumes')) {
return [
@ -673,6 +905,57 @@ export default {
name: 'label.migrating'
}
]
} else if (this.apiName.includes('listKubernetesClusters')) {
return [
{
id: 'Created',
name: 'label.created'
},
{
id: 'Starting',
name: 'label.starting'
},
{
id: 'Running',
name: 'label.running'
},
{
id: 'Stopping',
name: 'label.stopping'
},
{
id: 'Stopped',
name: 'label.stopped'
},
{
id: 'Scaling',
name: 'label.scaling'
},
{
id: 'Upgrading',
name: 'label.upgrading'
},
{
id: 'Alert',
name: 'label.alert'
},
{
id: 'Recovering',
name: 'label.recovering'
},
{
id: 'Destroyed',
name: 'label.destroyed'
},
{
id: 'Destroying',
name: 'label.destroying'
},
{
id: 'Error',
name: 'label.error'
}
]
}
return []
},

View File

@ -24,6 +24,7 @@ export default {
icon: 'team-outlined',
docHelp: 'adminguide/accounts.html',
permission: ['listAccounts'],
searchFilters: ['name', 'accounttype', 'domainid'],
columns: ['name', 'state', 'rolename', 'roletype', 'domainpath'],
details: ['name', 'id', 'rolename', 'roletype', 'domainpath', 'networkdomain', 'iptotal', 'vmtotal', 'volumetotal', 'receivedbytes', 'sentbytes', 'created'],
related: [{

View File

@ -520,6 +520,7 @@ export default {
title: 'label.kubernetes',
icon: ['fa-solid', 'fa-dharmachakra'],
docHelp: 'plugins/cloudstack-kubernetes-service.html',
searchFilters: ['name', 'domainid', 'account', 'state'],
permission: ['listKubernetesClusters'],
columns: (store) => {
var fields = ['name', 'state', 'clustertype', 'size', 'cpunumber', 'memory', 'kubernetesversionname']
@ -629,6 +630,7 @@ export default {
docHelp: 'adminguide/autoscale_with_virtual_router.html',
resourceType: 'AutoScaleVmGroup',
permission: ['listAutoScaleVmGroups'],
searchFilters: ['name', 'zoneid', 'domainid', 'account'],
columns: (store) => {
var fields = ['name', 'state', 'associatednetworkname', 'publicip', 'publicport', 'privateport', 'minmembers', 'maxmembers', 'availablevirtualmachinecount', 'account']
if (store.listAllProjects) {
@ -739,7 +741,7 @@ export default {
docHelp: 'adminguide/virtual_machines.html#changing-the-vm-name-os-or-group',
resourceType: 'VMInstanceGroup',
permission: ['listInstanceGroups'],
searchFilters: ['name', 'zoneid', 'domainid', 'account'],
columns: (store) => {
var fields = ['name', 'account']
if (store.listAllProjects) {
@ -797,6 +799,7 @@ export default {
icon: 'key-outlined',
docHelp: 'adminguide/virtual_machines.html#using-ssh-keys-for-authentication',
permission: ['listSSHKeyPairs'],
searchFilters: ['name', 'domainid', 'account', 'fingerprint'],
columns: () => {
var fields = ['name', 'fingerprint']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
@ -957,6 +960,7 @@ export default {
icon: 'swap-outlined',
docHelp: 'adminguide/virtual_machines.html#affinity-groups',
permission: ['listAffinityGroups'],
searchFilters: ['name', 'zoneid', 'domainid', 'account', 'type'],
columns: () => {
var fields = ['name', 'type', 'description']
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {

View File

@ -37,6 +37,7 @@ export default {
icon: 'team-outlined',
docHelp: 'adminguide/accounts.html#using-an-ldap-server-for-user-authentication',
permission: ['listLdapConfigurations'],
searchFilters: ['domainid', 'hostname', 'port'],
columns: ['hostname', 'port', 'domainid'],
details: ['hostname', 'port', 'domainid'],
actions: [
@ -118,6 +119,7 @@ export default {
icon: 'database-outlined',
docHelp: 'adminguide/hosts.html?highlight=Hypervisor%20capabilities#hypervisor-capabilities',
permission: ['listHypervisorCapabilities'],
searchFilters: ['hypervisor'],
columns: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxhostspercluster'],
details: ['hypervisor', 'hypervisorversion', 'maxguestslimit', 'maxdatavolumeslimit', 'maxhostspercluster', 'securitygroupenabled', 'storagemotionenabled'],
actions: [

View File

@ -367,6 +367,7 @@ export default {
icon: ['fa-solid', 'fa-dharmachakra'],
docHelp: 'plugins/cloudstack-kubernetes-service.html#kubernetes-supported-versions',
permission: ['listKubernetesSupportedVersions'],
searchFilters: ['zoneid', 'minimumsemanticversion'],
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'],
details: ['name', 'semanticversion', 'supportsautoscaling', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'mincpunumber', 'minmemory', 'supportsha', 'state', 'created'],
actions: [

View File

@ -79,7 +79,7 @@ export default {
permission: ['listAlerts'],
columns: ['name', 'description', 'type', 'sent'],
details: ['name', 'id', 'type', 'sent', 'description'],
searchFilters: ['type'],
searchFilters: ['name', 'type'],
actions: [
{
api: 'archiveAlerts',

View File

@ -24,6 +24,7 @@ export default {
icon: 'cluster-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-clusters',
permission: ['listClustersMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'hypervisor'],
columns: () => {
const fields = ['name', 'state', 'allocationstate', 'clustertype', 'hypervisortype', 'hosts']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance']

View File

@ -24,6 +24,7 @@ export default {
icon: 'database-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-hosts',
permission: ['listHostsMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'hypervisor'],
resourceType: 'Host',
filters: () => {
const filters = ['enabled', 'disabled', 'maintenance', 'up', 'down', 'alert']

View File

@ -24,6 +24,7 @@ export default {
icon: 'appstore-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-pods',
permission: ['listPods'],
searchFilters: ['name', 'zoneid'],
columns: ['name', 'allocationstate', 'gateway', 'netmask', 'zonename'],
details: ['name', 'id', 'allocationstate', 'netmask', 'gateway', 'zonename'],
related: [{

View File

@ -24,6 +24,7 @@ export default {
icon: 'hdd-outlined',
docHelp: 'adminguide/storage.html#primary-storage',
permission: ['listStoragePoolsMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'clusterid', 'ipaddress', 'path', 'scope'],
columns: () => {
const fields = ['name', 'state', 'ipaddress', 'scope', 'type', 'path']
const metricsFields = ['disksizeusedgb', 'disksizetotalgb', 'disksizeallocatedgb', 'disksizeunallocatedgb']

View File

@ -24,6 +24,7 @@ export default {
icon: 'picture-outlined',
docHelp: 'adminguide/storage.html#secondary-storage',
permission: ['listImageStores'],
searchFilters: ['name', 'zoneid', 'provider'],
columns: () => {
var fields = ['name', 'url', 'protocol', 'scope', 'zonename']
if (store.getters.apis.listImageStores.params.filter(x => x.name === 'readonly').length > 0) {

View File

@ -24,6 +24,7 @@ export default {
icon: 'thunderbolt-outlined',
docHelp: 'adminguide/systemvm.html',
permission: ['listSystemVms'],
searchFilters: ['name', 'zoneid', 'podid', 'hostid', 'systemvmtype', 'storageid'],
columns: ['name', 'state', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'version', 'hostname', 'zonename'],
details: ['name', 'id', 'agentstate', 'systemvmtype', 'publicip', 'privateip', 'linklocalip', 'gateway', 'hostname', 'version', 'zonename', 'created', 'activeviewersessions', 'isdynamicallyscalable', 'hostcontrolstate'],
resourceType: 'SystemVm',

View File

@ -24,6 +24,7 @@ export default {
icon: 'global-outlined',
docHelp: 'conceptsandterminology/concepts.html#about-zones',
permission: ['listZonesMetrics'],
searchFilters: ['name', 'domainid', 'tags'],
columns: () => {
const fields = ['name', 'allocationstate', 'type', 'networktype', 'clusters']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal']

View File

@ -748,6 +748,7 @@ export default {
icon: 'environment-outlined',
docHelp: 'adminguide/networking_and_traffic.html#reserving-public-ip-addresses-and-vlans-for-accounts',
permission: ['listPublicIpAddresses'],
searchFilters: ['ipaddress', 'zoneid', 'account', 'domainid', 'vlanid', 'tags'],
resourceType: 'PublicIpAddress',
columns: () => {
var fields = ['ipaddress', 'state', 'associatednetworkname', 'vpcname', 'virtualmachinename', 'allocated', 'account']
@ -921,7 +922,6 @@ export default {
name: 's2svpn',
title: 'label.site.to.site.vpn',
icon: 'lock-outlined',
hidden: true,
permission: ['listVpnGateways'],
columns: ['publicip', 'account', 'domain'],
details: ['publicip', 'account', 'domain'],

View File

@ -29,6 +29,7 @@ export default {
docHelp: 'adminguide/service_offerings.html#compute-and-disk-service-offerings',
icon: 'cloud-outlined',
permission: ['listServiceOfferings'],
searchFilters: ['name', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'],
params: () => {
var params = {}
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
@ -139,6 +140,7 @@ export default {
icon: 'setting-outlined',
docHelp: 'adminguide/service_offerings.html#system-service-offerings',
permission: ['listServiceOfferings', 'listInfrastructure'],
searchFilters: ['name', 'zoneid', 'domainid', 'cpunumber', 'cpuspeed', 'memory'],
params: { issystem: 'true', isrecursive: 'true' },
columns: ['name', 'state', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'order'],
filters: ['active', 'inactive'],
@ -215,6 +217,7 @@ export default {
icon: 'hdd-outlined',
docHelp: 'adminguide/service_offerings.html#compute-and-disk-service-offerings',
permission: ['listDiskOfferings'],
searchFilters: ['name', 'zoneid', 'domainid', 'storageid'],
params: () => {
var params = {}
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
@ -314,6 +317,7 @@ export default {
icon: 'cloud-upload-outlined',
docHelp: 'adminguide/virtual_machines.html#backup-offerings',
permission: ['listBackupOfferings'],
searchFilters: ['zoneid'],
columns: ['name', 'description', 'zonename'],
details: ['name', 'id', 'description', 'externalid', 'zone', 'allowuserdrivenbackups', 'created'],
related: [{
@ -367,6 +371,7 @@ export default {
icon: 'wifi-outlined',
docHelp: 'adminguide/networking.html#network-offerings',
permission: ['listNetworkOfferings'],
searchFilters: ['name', 'zoneid', 'domainid', 'tags'],
columns: ['name', 'state', 'guestiptype', 'traffictype', 'networkrate', 'domain', 'zone', 'order'],
details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'internetprotocol', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'forvpc', 'fornsx', 'nsxmode', 'service', 'tags', 'domain', 'zone'],
resourceType: 'NetworkOffering',
@ -465,6 +470,7 @@ export default {
icon: 'deployment-unit-outlined',
docHelp: 'plugins/nuage-plugin.html?#vpc-offerings',
permission: ['listVPCOfferings'],
searchFilters: ['name', 'zoneid', 'domainid'],
resourceType: 'VpcOffering',
columns: ['name', 'state', 'displaytext', 'domain', 'zone', 'order'],
details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'service', 'fornsx', 'nsxmode', 'domain', 'zone', 'created'],

View File

@ -24,6 +24,7 @@ export default {
icon: 'idcard-outlined',
docHelp: 'adminguide/accounts.html#roles',
permission: ['listRoles', 'listRolePermissions'],
searchFilters: ['name', 'type'],
columns: ['name', 'type', 'description'],
details: ['name', 'id', 'type', 'description', 'ispublic'],
tabs: [{

View File

@ -167,6 +167,8 @@ import {
UserOutlined,
UserSwitchOutlined,
UploadOutlined,
VerticalAlignBottomOutlined,
VerticalAlignTopOutlined,
WarningOutlined,
WifiOutlined,
SolutionOutlined
@ -326,6 +328,8 @@ export default {
app.component('UserOutlined', UserOutlined)
app.component('UserSwitchOutlined', UserSwitchOutlined)
app.component('UploadOutlined', UploadOutlined)
app.component('VerticalAlignBottomOutlined', VerticalAlignBottomOutlined)
app.component('VerticalAlignTopOutlined', VerticalAlignTopOutlined)
app.component('WarningOutlined', WarningOutlined)
app.component('WifiOutlined', WifiOutlined)
app.component('renderIcon', renderIcon)

View File

@ -392,7 +392,7 @@
<template #description>
<div v-if="zoneSelected" style="margin-top: 5px">
<div style="margin-bottom: 10px">
{{ $t('message.network.selection') }}
{{ $t('message.network.selection') + ('createNetwork' in $store.getters.apis ? ' ' + $t('message.network.selection.new.network') : '') }}
</div>
<div v-if="vm.templateid && templateNics && templateNics.length > 0">
<instance-nics-network-select-list-view

View File

@ -254,7 +254,7 @@ export default {
api('listZones', { id: this.zoneId }).then(json => {
const zoneResponse = json.listzonesresponse.zone || []
this.showCreateButton = false
if (zoneResponse && zoneResponse.length > 0 && (!zoneResponse[0].securitygroupsenabled || (isAdmin() && zoneResponse[0].networktype === 'Advanced'))) {
if ('createNetwork' in store.getters.apis && zoneResponse && zoneResponse.length > 0 && (!zoneResponse[0].securitygroupsenabled || (isAdmin() && zoneResponse[0].networktype === 'Advanced'))) {
this.showCreateButton = true
}
})

View File

@ -90,6 +90,16 @@
</div>
</div>
<div class="list__actions">
<tooltip-button
v-if="element.id !== acls[0].id"
:tooltip="$t('label.move.to.top')"
icon="vertical-align-top-outlined"
@onClick="() => moveRuleToTop(element)" />
<tooltip-button
v-if="element.id !== acls[acls.length - 1].id"
:tooltip="$t('label.move.to.bottom')"
icon="vertical-align-bottom-outlined"
@onClick="() => moveRuleToBottom(element)" />
<tooltip-button :tooltip="$t('label.tags')" icon="tag-outlined" @onClick="() => openTagsModal(element)" />
<tooltip-button :tooltip="$t('label.edit')" icon="edit-outlined" @onClick="() => openEditRuleModal(element)" />
<tooltip-button
@ -358,6 +368,10 @@ export default {
}
keys = Object.keys(data[0])
for (var i = 1; i < data.length; ++i) {
const rowKeys = Object.keys(data[i])
keys = keys.concat(rowKeys.filter(k => !keys.includes(k)))
}
result = ''
result += keys.join(columnDelimiter)
@ -705,6 +719,12 @@ export default {
if (e.moved.newIndex + 1 < this.acls.length) nextaclruleid = this.acls[e.moved.newIndex + 1].id
this.moveRule(
id,
previousaclruleid,
nextaclruleid)
},
moveRule (id, previousaclruleid, nextaclruleid) {
this.fetchLoading = true
api('moveNetworkAclItem', {
id,
@ -737,6 +757,12 @@ export default {
this.fetchLoading = false
})
},
moveRuleToTop (element) {
this.moveRule(element.id, null, this.acls[0].id)
},
moveRuleToBottom (element) {
this.moveRule(element.id, this.acls[this.acls.length - 1].id, null)
},
exportAclList () {
const csvData = this.csv({ data: this.acls })