Merge remote-tracking branch 'apache/4.20' into 4.22

This commit is contained in:
Abhishek Kumar 2026-01-30 10:08:36 +05:30
commit 29ce03e946
No known key found for this signature in database
GPG Key ID: 26DF259080DABDC4
7 changed files with 694 additions and 59 deletions

View File

@ -4775,12 +4775,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
String dataDiskController = details.get(VmDetailConstants.DATA_DISK_CONTROLLER);
if (StringUtils.isNotBlank(dataDiskController)) {
LOGGER.debug("Passed custom disk controller for DATA disk " + dataDiskController);
for (DiskDef.DiskBus bus : DiskDef.DiskBus.values()) {
if (bus.toString().equalsIgnoreCase(dataDiskController)) {
LOGGER.debug("Found matching enum for disk controller for DATA disk " + dataDiskController);
return bus;
}
LOGGER.debug("Passed custom disk controller for DATA disk {}", dataDiskController);
DiskDef.DiskBus bus = DiskDef.DiskBus.fromValue(dataDiskController);
if (bus != null) {
LOGGER.debug("Found matching enum for disk controller for DATA disk {}", dataDiskController);
return bus;
}
}
return null;

View File

@ -686,6 +686,15 @@ public class LibvirtVMDef {
_bus = bus;
}
public static DiskBus fromValue(String bus) {
for (DiskBus b : DiskBus.values()) {
if (b.toString().equalsIgnoreCase(bus)) {
return b;
}
}
return null;
}
@Override
public String toString() {
return _bus;

View File

@ -1374,26 +1374,27 @@ public class KVMStorageProcessor implements StorageProcessor {
/**
* Attaches or detaches a disk to an instance.
* @param conn libvirt connection
* @param attach boolean that determines whether the device will be attached or detached
* @param vmName instance name
* @param attachingDisk kvm physical disk
* @param devId device id in instance
* @param conn libvirt connection
* @param attach boolean that determines whether the device will be attached or detached
* @param vmName instance name
* @param attachingDisk kvm physical disk
* @param devId device id in instance
* @param serial
* @param bytesReadRate bytes read rate
* @param bytesReadRateMax bytes read rate max
* @param bytesReadRateMaxLength bytes read rate max length
* @param bytesWriteRate bytes write rate
* @param bytesWriteRateMax bytes write rate amx
* @param bytesReadRate bytes read rate
* @param bytesReadRateMax bytes read rate max
* @param bytesReadRateMaxLength bytes read rate max length
* @param bytesWriteRate bytes write rate
* @param bytesWriteRateMax bytes write rate amx
* @param bytesWriteRateMaxLength bytes write rate max length
* @param iopsReadRate iops read rate
* @param iopsReadRateMax iops read rate max
* @param iopsReadRateMaxLength iops read rate max length
* @param iopsWriteRate iops write rate
* @param iopsWriteRateMax iops write rate max
* @param iopsWriteRateMaxLength iops write rate max length
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param iopsReadRate iops read rate
* @param iopsReadRateMax iops read rate max
* @param iopsReadRateMaxLength iops read rate max length
* @param iopsWriteRate iops write rate
* @param iopsWriteRateMax iops write rate max
* @param iopsWriteRateMaxLength iops write rate max length
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param controllerInfo
* @throws LibvirtException
* @throws InternalErrorException
*/
@ -1401,37 +1402,38 @@ public class KVMStorageProcessor implements StorageProcessor {
final String serial, final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength,
final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map<String, String> details)
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map<String, String> details, Map<String, String> controllerInfo)
throws LibvirtException, InternalErrorException {
attachOrDetachDisk(conn, attach, vmName, attachingDisk, devId, serial, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength,
bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate,
iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details);
iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details, controllerInfo);
}
/**
*
* Attaches or detaches a disk to an instance.
* @param conn libvirt connection
* @param attach boolean that determines whether the device will be attached or detached
* @param vmName instance name
* @param attachingDisk kvm physical disk
* @param devId device id in instance
* @param conn libvirt connection
* @param attach boolean that determines whether the device will be attached or detached
* @param vmName instance name
* @param attachingDisk kvm physical disk
* @param devId device id in instance
* @param serial
* @param bytesReadRate bytes read rate
* @param bytesReadRateMax bytes read rate max
* @param bytesReadRateMaxLength bytes read rate max length
* @param bytesWriteRate bytes write rate
* @param bytesWriteRateMax bytes write rate amx
* @param bytesReadRate bytes read rate
* @param bytesReadRateMax bytes read rate max
* @param bytesReadRateMaxLength bytes read rate max length
* @param bytesWriteRate bytes write rate
* @param bytesWriteRateMax bytes write rate amx
* @param bytesWriteRateMaxLength bytes write rate max length
* @param iopsReadRate iops read rate
* @param iopsReadRateMax iops read rate max
* @param iopsReadRateMaxLength iops read rate max length
* @param iopsWriteRate iops write rate
* @param iopsWriteRateMax iops write rate max
* @param iopsWriteRateMaxLength iops write rate max length
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param waitDetachDevice value set in milliseconds to wait before assuming device removal failed
* @param iopsReadRate iops read rate
* @param iopsReadRateMax iops read rate max
* @param iopsReadRateMaxLength iops read rate max length
* @param iopsWriteRate iops write rate
* @param iopsWriteRateMax iops write rate max
* @param iopsWriteRateMaxLength iops write rate max length
* @param cacheMode cache mode
* @param encryptDetails encrypt details
* @param waitDetachDevice value set in milliseconds to wait before assuming device removal failed
* @param controllerInfo
* @throws LibvirtException
* @throws InternalErrorException
*/
@ -1440,7 +1442,7 @@ public class KVMStorageProcessor implements StorageProcessor {
final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate,
final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax,
final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails,
long waitDetachDevice, Map<String, String> details)
long waitDetachDevice, Map<String, String> details, Map<String, String> controllerInfo)
throws LibvirtException, InternalErrorException {
List<DiskDef> disks = null;
@ -1477,17 +1479,7 @@ public class KVMStorageProcessor implements StorageProcessor {
return;
}
} else {
DiskDef.DiskBus busT = DiskDef.DiskBus.VIRTIO;
for (final DiskDef disk : disks) {
if (disk.getDeviceType() == DeviceType.DISK) {
if (disk.getBusType() == DiskDef.DiskBus.SCSI) {
busT = DiskDef.DiskBus.SCSI;
} else if (disk.getBusType() == DiskDef.DiskBus.VIRTIOBLK) {
busT = DiskDef.DiskBus.VIRTIOBLK;
}
break;
}
}
DiskDef.DiskBus busT = getAttachDiskBusType(devId, disks, controllerInfo);
diskdef = new DiskDef();
if (busT == DiskDef.DiskBus.SCSI || busT == DiskDef.DiskBus.VIRTIOBLK) {
diskdef.setQemuDriver(true);
@ -1592,6 +1584,28 @@ public class KVMStorageProcessor implements StorageProcessor {
}
}
protected DiskDef.DiskBus getAttachDiskBusType(int deviceId, List<DiskDef> disks, Map<String, String> controllerInfo) {
String controllerKey = deviceId == 0 ? VmDetailConstants.ROOT_DISK_CONTROLLER : VmDetailConstants.DATA_DISK_CONTROLLER;
String diskController = MapUtils.getString(controllerInfo, controllerKey);
DiskDef.DiskBus busType = DiskDef.DiskBus.fromValue(diskController);
if (diskController != null) {
logger.debug("Using controller '{}' from command specified as {} while attaching disk (deviceId={})",
diskController, controllerKey, deviceId);
return busType;
}
for (final DiskDef disk : disks) {
if (disk.getDeviceType() != DeviceType.DISK) {
continue;
}
if (disk.getBusType() == DiskDef.DiskBus.SCSI) {
return DiskDef.DiskBus.SCSI;
} else if (disk.getBusType() == DiskDef.DiskBus.VIRTIOBLK) {
return DiskDef.DiskBus.VIRTIOBLK;
}
}
return DiskDef.DiskBus.VIRTIO;
}
@Override
public Answer attachVolume(final AttachCommand cmd) {
final DiskTO disk = cmd.getDisk();
@ -1619,7 +1633,8 @@ public class KVMStorageProcessor implements StorageProcessor {
vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, encryptDetails, disk.getDetails());
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode,
encryptDetails, disk.getDetails(), cmd.getControllerInfo());
resource.recreateCheckpointsOnVm(List.of((VolumeObjectTO) disk.getData()), vmName, conn);
@ -1658,7 +1673,7 @@ public class KVMStorageProcessor implements StorageProcessor {
vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(),
vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(),
vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(),
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null);
vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null, null);
storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath());

View File

@ -105,6 +105,7 @@
"showAllCategoryForModernImageSelection": false,
"docHelpMappings": {},
"notifyLatestCSVersion": true,
"showSearchFilters": true,
"announcementBanner": {
"enabled": false,
"showIcon": false,

View File

@ -0,0 +1,559 @@
// 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>
<a-row
style="overflow: auto"
:wrap="false"
>
<template
v-for="filter in this.searchFilters"
:key="filter.key + filter.value"
>
<a-col v-if="!['page', 'pagesize', 'q', 'keyword', 'tags'].includes(filter.key)">
<a-tag
v-if="!filter.isTag"
closable
@close="() => $emit('removeFilter', filter)"
>
<a-tooltip
:title="retrieveFieldLabel(filter.key) + ': ' + filter.value"
placement="bottom"
>
{{ retrieveFieldLabel(filter.key) }} : {{ getTrimmedText(filter.value, 20) }}
</a-tooltip>
</a-tag>
<a-tag
v-else
closable
@close="() => $emit('removeFilter', filter)"
>
<a-tooltip
:title="$t('label.tag') + ': ' + filter.key + '=' + filter.value"
placement="bottom"
>
{{ $t('label.tag') }}: {{ filter.key }}={{ getTrimmedText(filter.value, 20) }}
</a-tooltip>
</a-tag>
</a-col>
</template>
</a-row>
</template>
<script>
import { api } from '@/api/index'
export default {
name: 'SearchFilter',
props: {
filters: {
type: Array,
default: () => []
},
apiName: {
type: String,
default: () => ''
},
filterKey: {
type: String,
default: () => ''
},
filterValue: {
type: String,
default: () => ''
},
isTag: {
type: Boolean,
default: () => false
}
},
emits: ['removeFilter'],
data () {
return {
searchFilters: [],
apiMap: {
type: (value) => this.getType(value),
hypervisor: (value) => this.getHypervisor(value),
zoneid: {
apiName: 'listZones',
responseKey1: 'listzonesresponse',
responseKey2: 'zone',
field: 'name'
},
domainid: {
apiName: 'listDomains',
responseKey1: 'listdomainsresponse',
responseKey2: 'domain',
field: 'name'
},
account: {
apiName: 'listAccounts',
responseKey1: 'listaccountsresponse',
responseKey2: 'account',
field: 'name'
},
imagestoreid: {
apiName: 'listImageStores',
responseKey1: 'listimagestoresresponse',
responseKey2: 'imagestore',
field: 'name'
},
storageid: {
apiName: 'listStoragePools',
responseKey1: 'liststoragepoolsresponse',
responseKey2: 'storagepool',
field: 'name'
},
podid: {
apiName: 'listPods',
responseKey1: 'listpodsresponse',
responseKey2: 'pod',
field: 'name'
},
clusterid: {
apiName: 'listClusters',
responseKey1: 'listclustersresponse',
responseKey2: 'cluster',
field: 'name'
},
hostid: {
apiName: 'listHosts',
responseKey1: 'listhostsresponse',
responseKey2: 'host',
field: 'name'
},
groupid: {
apiName: 'listInstanceGroups',
responseKey1: 'listinstancegroupsresponse',
responseKey2: 'instancegroup',
field: 'name'
},
managementserverid: {
apiName: 'listManagementServers',
responseKey1: 'listmanagementserversresponse',
responseKey2: 'managementserver',
field: 'name'
},
serviceofferingid: {
apiName: 'listServiceOfferings',
responseKey1: 'listserviceofferingsresponse',
responseKey2: 'serviceoffering',
field: 'name'
},
diskofferingid: {
apiName: 'listDiskOfferings',
responseKey1: 'listdiskofferingsresponse',
responseKey2: 'diskoffering',
field: 'name'
},
networkid: {
apiName: 'listNetworks',
responseKey1: 'listnetworksresponse',
responseKey2: 'network',
field: 'name'
}
}
}
},
watch: {
filters: {
immediate: true,
handler (newFilters) {
const clonedFilters = newFilters.map(filter => ({ ...filter }))
const promises = []
for (let idx = 0; idx < clonedFilters.length; idx++) {
const filter = clonedFilters[idx]
promises.push(new Promise((resolve) => {
if (filter.key === 'tags') {
clonedFilters[idx] = {
key: filter.key,
value: filter.value,
isTag: true
}
resolve()
} else {
this.getSearchFilters(filter.key, filter.value).then((value) => {
clonedFilters[idx] = {
key: filter.key,
value: value,
isTag: filter.isTag
}
resolve()
})
}
}))
}
Promise.all(promises).then(() => {
this.searchFilters = clonedFilters
})
}
}
},
methods: {
getTrimmedText (text, length) {
if (!text) {
return ''
}
return (text.length <= length) ? text : (text.substring(0, length - 3) + '...')
},
retrieveFieldLabel (fieldName) {
if (fieldName === 'groupid') {
fieldName = 'group'
}
if (fieldName === 'keyword') {
if ('listAnnotations' in this.$store.getters.apis) {
return this.$t('label.annotation')
} else {
return this.$t('label.name')
}
}
return this.$t('label.' + fieldName)
},
async getSearchFilters (key, value) {
const val = this.getStaticFieldValue(key, value)
if (val !== '') {
return val
} else {
return this.getDynamicFieldValue(key, value)
}
},
getStaticFieldValue (key, value) {
let formattedValue = ''
if (key.includes('type')) {
if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) {
formattedValue = this.getGuestNetworkType(value)
} else if (this.$route.path === '/role' || this.$route.path.includes('/role/')) {
formattedValue = this.getRoleType(value)
}
}
if (key.includes('scope')) {
// Check storage pool scope first (more specific), then fall back to general scope
const storagePoolScope = this.getStoragePoolScope(value)
formattedValue = storagePoolScope && storagePoolScope.length > 0 ? storagePoolScope : this.getScope(value)
}
if (key.includes('state')) {
formattedValue = this.getState(value)
}
if (key.includes('level')) {
formattedValue = this.getLevel(value)
}
if (key.includes('entitytype')) {
formattedValue = this.getEntityType(value)
}
if (key.includes('accounttype')) {
formattedValue = this.getAccountType(value)
}
if (key.includes('systemvmtype')) {
formattedValue = this.getSystemVmType(value)
}
if (key.includes('provider')) {
formattedValue = this.getImageStoreProvider(value)
}
if (key.includes('resourcetype')) {
formattedValue = value
}
return formattedValue
},
async getDynamicFieldValue (key, value) {
let formattedValue = ''
if (typeof this.apiMap[key] === 'function') {
formattedValue = await this.apiMap[key](value)
} else if (this.apiMap[key]) {
const apiName = this.apiMap[key].apiName
const responseKey1 = this.apiMap[key].responseKey1
const responseKey2 = this.apiMap[key].responseKey2
const field = this.apiMap[key].field
formattedValue = await this.getResourceNameById(apiName, responseKey1, responseKey2, value, field)
}
if (formattedValue === '') {
formattedValue = value
}
return formattedValue
},
getHypervisor (value) {
return new Promise((resolve) => {
api('listHypervisors').then(json => {
if (json?.listhypervisorsresponse?.hypervisor) {
for (const key in json.listhypervisorsresponse.hypervisor) {
const hypervisor = json.listhypervisorsresponse.hypervisor[key]
if (hypervisor.name === value) {
return resolve(hypervisor.name)
}
}
}
resolve(null)
}).catch(() => {
resolve(null)
})
})
},
getResourceNameById (apiName, responseKey1, responseKey2, id, field) {
return new Promise((resolve) => {
if (!this.$isValidUuid(id)) {
return resolve('')
}
api(apiName, { listAll: true, id: id }).then(json => {
const items = json && json[responseKey1] && json[responseKey1][responseKey2]
if (Array.isArray(items) && items.length > 0 && items[0] && items[0][field] !== undefined) {
resolve(items[0][field])
} else {
resolve('')
}
}).catch(() => {
resolve('')
})
})
},
getType (type) {
if (this.$route.path === '/alert') {
return this.getAlertType(type)
} else if (this.$route.path === '/affinitygroup') {
return this.getAffinityGroupType(type)
}
},
getAlertType (type) {
return new Promise((resolve) => {
api('listAlertTypes').then(json => {
const alertTypes = {}
for (const key in json.listalerttypesresponse.alerttype) {
const alerttype = json.listalerttypesresponse.alerttype[key]
alertTypes[alerttype.id] = alerttype.name
}
resolve(alertTypes[type])
}).catch(() => {
resolve(null)
})
})
},
getAffinityGroupType (type) {
return new Promise((resolve) => {
api('listAffinityGroupTypes').then(json => {
const alertTypes = {}
for (const key in json.listaffinitygrouptypesresponse.affinityGroupType) {
const affinityGroupType = json.listaffinitygrouptypesresponse.affinityGroupType[key]
if (affinityGroupType.type === 'host anti-affinity') {
alertTypes[affinityGroupType.type] = 'host anti-affinity (Strict)'
} else if (affinityGroupType.type === 'host affinity') {
alertTypes[affinityGroupType.type] = 'host affinity (Strict)'
} else if (affinityGroupType.type === 'non-strict host anti-affinity') {
alertTypes[affinityGroupType.type] = 'host anti-affinity (Non-Strict)'
} else if (affinityGroupType.type === 'non-strict host affinity') {
alertTypes[affinityGroupType.type] = 'host affinity (Non-Strict)'
}
}
this.alertTypes = alertTypes
resolve(alertTypes[type])
}).catch(() => {
resolve(null)
})
})
},
getGuestNetworkType (value) {
switch (value.toLowerCase()) {
case 'isolated':
return this.$t('label.isolated')
case 'shared':
return this.$t('label.shared')
case 'l2':
return this.$t('label.l2')
}
},
getAccountType (type) {
const types = []
if (this.apiName.indexOf('listAccounts') > -1) {
switch (type) {
case '1':
return 'Admin'
case '2':
return 'DomainAdmin'
case '0':
return 'User'
}
}
return types
},
getSystemVmType (type) {
if (this.apiName.indexOf('listSystemVms') > -1) {
switch (type.toLowerCase()) {
case 'consoleproxy':
return this.$t('label.console.proxy.vm')
case 'secondarystoragevm':
return this.$t('label.secondary.storage.vm')
}
}
},
getStoragePoolScope (scope) {
if (this.apiName.indexOf('listStoragePools') > -1) {
switch (scope.toUpperCase()) {
case 'CLUSTER':
return this.$t('label.cluster')
case 'ZONE':
return this.$t('label.zone')
case 'REGION':
return this.$t('label.region')
case 'GLOBAL':
return this.$t('label.global')
}
}
},
getImageStoreProvider (provider) {
if (this.apiName.indexOf('listImageStores') > -1) {
switch (provider.toUpperCase()) {
case 'NFS':
return 'NFS'
case 'SMB':
return 'SMB/CIFS'
case 'S3':
return 'S3'
case 'SWIFT':
return 'Swift'
}
}
},
getRoleType (role) {
switch (role.toLowerCase()) {
case 'Admin'.toLowerCase():
return 'Admin'
case 'ResourceAdmin'.toLowerCase():
return 'ResourceAdmin'
case 'DomainAdmin'.toLowerCase():
return 'DomainAdmin'
case 'User'.toLowerCase():
return 'User'
}
},
getScope (scope) {
switch (scope.toLowerCase()) {
case 'local':
return this.$t('label.local')
case 'domain':
return this.$t('label.domain')
case 'global':
return this.$t('label.global')
}
},
getState (state) {
if (this.apiName.includes('listVolumes')) {
switch (state.toLowerCase()) {
case 'allocated':
return this.$t('label.allocated')
case 'ready':
return this.$t('label.isready')
case 'destroy':
return this.$t('label.destroy')
case 'expunging':
return this.$t('label.expunging')
case 'expunged':
return this.$t('label.expunged')
case 'migrating':
return this.$t('label.migrating')
}
} else if (this.apiName.includes('listKubernetesClusters')) {
switch (state.toLowerCase()) {
case 'created':
return this.$t('label.created')
case 'starting':
return this.$t('label.starting')
case 'running':
return this.$t('label.running')
case 'stopping':
return this.$t('label.stopping')
case 'stopped':
return this.$t('label.stopped')
case 'scaling':
return this.$t('label.scaling')
case 'upgrading':
return this.$t('label.upgrading')
case 'alert':
return this.$t('label.alert')
case 'recovering':
return this.$t('label.recovering')
case 'destroyed':
return this.$t('label.destroyed')
case 'destroying':
return this.$t('label.destroying')
case 'error':
return this.$t('label.error')
}
} else if (this.apiName.indexOf('listWebhooks') > -1) {
switch (state.toLowerCase()) {
case 'enabled':
return this.$t('label.enabled')
case 'disabled':
return this.$t('label.disabled')
}
}
},
getEntityType (type) {
let entityType = ''
if (this.apiName.indexOf('listAnnotations') > -1) {
const allowedTypes = {
VM: 'Virtual Machine',
HOST: 'Host',
VOLUME: 'Volume',
SNAPSHOT: 'Snapshot',
VM_SNAPSHOT: 'VM Snapshot',
INSTANCE_GROUP: 'Instance Group',
NETWORK: 'Network',
VPC: 'VPC',
PUBLIC_IP_ADDRESS: 'Public IP Address',
VPN_CUSTOMER_GATEWAY: 'VPC Customer Gateway',
TEMPLATE: 'Template',
ISO: 'ISO',
SSH_KEYPAIR: 'SSH Key Pair',
DOMAIN: 'Domain',
SERVICE_OFFERING: 'Service Offering',
DISK_OFFERING: 'Disk Offering',
NETWORK_OFFERING: 'Network Offering',
POD: 'Pod',
ZONE: 'Zone',
CLUSTER: 'Cluster',
PRIMARY_STORAGE: 'Primary Storage',
SECONDARY_STORAGE: 'Secondary Storage',
VR: 'Virtual Router',
SYSTEM_VM: 'System VM',
KUBERNETES_CLUSTER: 'Kubernetes Cluster'
}
entityType = allowedTypes[type.toUpperCase()]
}
return entityType
},
getLevel (level) {
switch (level.toUpperCase()) {
case 'INFO':
return this.$t('label.info.upper')
case 'WARN':
return this.$t('label.warn.upper')
case 'ERROR':
return this.$t('label.error.upper')
}
}
}
}
</script>

View File

@ -126,6 +126,16 @@
/>
</a-col>
</a-row>
<a-row
v-if="!dataView && $config.showSearchFilters"
style="min-height: 36px; padding-top: 12px;"
>
<search-filter
:filters="getActiveFilters()"
:apiName="apiName"
@removeFilter="removeFilter"
/>
</a-row>
</a-card>
</a-affix>
@ -599,6 +609,7 @@ import ListView from '@/components/view/ListView'
import ResourceView from '@/components/view/ResourceView'
import ActionButton from '@/components/view/ActionButton'
import SearchView from '@/components/view/SearchView'
import SearchFilter from '@/components/view/SearchFilter'
import OsLogo from '@/components/widgets/OsLogo'
import ResourceIcon from '@/components/view/ResourceIcon'
import BulkActionProgress from '@/components/view/BulkActionProgress'
@ -613,6 +624,7 @@ export default {
ListView,
ActionButton,
SearchView,
SearchFilter,
BulkActionProgress,
TooltipLabel,
OsLogo,
@ -1257,6 +1269,42 @@ export default {
eventBus.emit('action-closing', { action: this.currentAction })
this.closeAction()
},
getActiveFilters () {
const queryParams = Object.assign({}, this.$route.query)
const activeFilters = []
for (const filter in queryParams) {
if (!filter.startsWith('tags[')) {
activeFilters.push({
key: filter,
value: queryParams[filter],
isTag: false
})
} else if (filter.endsWith('].key')) {
const tagIdx = filter.split('[')[1].split(']')[0]
const tagKey = queryParams[`tags[${tagIdx}].key`]
const tagValue = queryParams[`tags[${tagIdx}].value`]
activeFilters.push({
key: tagKey,
value: tagValue,
isTag: true,
tagIdx: tagIdx
})
}
}
return activeFilters
},
removeFilter (filter) {
const queryParams = Object.assign({}, this.$route.query)
if (filter.isTag) {
delete queryParams[`tags[${filter.tagIdx}].key`]
delete queryParams[`tags[${filter.tagIdx}].value`]
} else {
delete queryParams[filter.key]
}
queryParams.page = '1'
queryParams.pagesize = String(this.pageSize)
this.$router.push({ query: queryParams })
},
onRowSelectionChange (selection) {
this.selectedRowKeys = selection
if (selection?.length > 0) {

View File

@ -113,6 +113,10 @@ store = common.createMockStore(state, actions, mutations)
i18n = common.createMockI18n('en', mockData.messages)
mocks = {
$config: {
showSearchFilters: true,
docBase: 'http://docs.cloudstack.apache.org/en/latest'
},
$notifyError: jest.fn((error) => {
return error
}),