This commit is contained in:
Abhishek Kumar 2026-03-09 14:14:41 +01:00 committed by GitHub
commit 74b0e2733f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 117 additions and 7 deletions

View File

@ -303,6 +303,7 @@ public class ApiConstants {
public static final String NEXT_ACL_RULE_ID = "nextaclruleid";
public static final String NEXT_HOP = "nexthop";
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
public static final String IMAGE_CACHE_STORES = "imagecachestores";
public static final String IMAGE_PATH = "imagepath";
public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported";
public static final String INTERNAL_DNS1 = "internaldns1";

View File

@ -40,6 +40,8 @@ public interface ImageStoreDao extends GenericDao<ImageStoreVO, Long> {
List<ImageStoreVO> listImageStores();
Integer countAllImageCacheStores();
List<ImageStoreVO> listImageCacheStores();
List<ImageStoreVO> listStoresByZoneId(long zoneId);

View File

@ -174,6 +174,13 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
return listBy(sc);
}
@Override
public Integer countAllImageCacheStores() {
SearchCriteria<ImageStoreVO> sc = createSearchCriteria();
sc.addAnd("role", SearchCriteria.Op.EQ, DataStoreRole.Image);
return getCount(sc);
}
@Override
public List<ImageStoreVO> listImageCacheStores() {
SearchCriteria<ImageStoreVO> sc = createSearchCriteria();

View File

@ -652,6 +652,7 @@ public class MetricsServiceImpl extends MutualExclusiveIdsManagerBase implements
response.setHosts(hostCountAndCpuSockets.first());
response.setStoragePools(storagePoolDao.countAll());
response.setImageStores(imageStoreDao.countAllImageStores());
response.setImageCacheStores(imageStoreDao.countAllImageCacheStores());
response.setBackupRepositories(backupRepositoryDao.countAll());
response.setObjectStores(objectStoreDao.countAllObjectStores());
response.setSystemvms(vmInstanceDao.countByTypes(VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm));

View File

@ -19,6 +19,8 @@ package org.apache.cloudstack.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
public class InfrastructureResponse extends BaseResponse {
@ -79,6 +81,10 @@ public class InfrastructureResponse extends BaseResponse {
@Param(description = "Number of Alerts")
private Integer alerts;
@SerializedName(ApiConstants.IMAGE_CACHE_STORES)
@Param(description = "Number of image cache stores", since = "4.22.0")
private Integer imageCacheStores;
public InfrastructureResponse() {
setObjectName("infrastructure");
}
@ -134,4 +140,8 @@ public class InfrastructureResponse extends BaseResponse {
public void setObjectStores(Integer objectStores) {
this.objectStores = objectStores;
}
public void setImageCacheStores(Integer imageCacheStores) {
this.imageCacheStores = imageCacheStores;
}
}

View File

@ -111,6 +111,7 @@
"label.action.delete.pod": "Delete Pod",
"label.action.delete.primary.storage": "Delete Primary Storage",
"label.action.delete.routing.firewall.rule": "Delete IPv4 Routing firewall rule",
"label.action.delete.secondary.staging.storage": "Delete Secondary Staging Storage",
"label.action.delete.secondary.storage": "Delete Secondary Storage",
"label.action.delete.security.group": "Delete Security Group",
"label.action.delete.snapshot": "Delete Snapshot",
@ -326,6 +327,7 @@
"label.add.routing.policy": "Add Routing Policy",
"label.add.rule": "Add Rule",
"label.add.secondary.ip": "Add Secondary IP",
"label.add.secondary.staging.storage": "Add Secondary Staging Storage",
"label.add.secondary.storage": "Add Secondary Storage",
"label.add.security.group": "Add Security Group",
"label.add.setting": "Add setting",
@ -2228,6 +2230,7 @@
"label.search": "Search",
"label.secondary.isolated.vlan.type.isolated": "Isolated",
"label.secondary.isolated.vlan.type.promiscuous": "Promiscuous",
"label.secondary.staging.storage": "Secondary Staging Storage",
"label.secondary.storage": "Secondary Storage",
"label.secondary.storage.vm": "Secondary Storage VM",
"label.secondaryips": "Secondary IPs",
@ -2963,6 +2966,7 @@
"message.action.delete.oauth.provider": "Please confirm that you want to delete the OAuth provider.",
"message.action.delete.physical.network": "Please confirm that you want to delete this physical Network.",
"message.action.delete.pod": "Please confirm that you want to delete this Pod.",
"message.action.delete.secondary.staging.storage": "Please confirm that you want to delete this secondary staging storage.",
"message.action.delete.secondary.storage": "Please confirm that you want to delete this secondary storage.",
"message.action.delete.security.group": "Please confirm that you want to delete this security group.",
"message.action.delete.snapshot": "Please confirm that you want to delete this Snapshot.",

View File

@ -66,6 +66,7 @@ function generateRouterMap (section) {
if ('show' in child && !child.show()) {
continue
}
console.log('Generating route for child:', child.name)
var component = child.component ? child.component : shallowRef(AutogenView)
var route = {
name: child.name,

View File

@ -23,6 +23,7 @@ import clusters from '@/config/section/infra/clusters'
import hosts from '@/config/section/infra/hosts'
import primaryStorages from '@/config/section/infra/primaryStorages'
import secondaryStorages from '@/config/section/infra/secondaryStorages'
import secondaryStagingStorages from '@/config/section/infra/secondaryStagingStorages'
import backupRepositories from '@/config/section/infra/backupRepositories'
import objectStorages from '@/config/section/infra/objectStorages'
import systemVms from '@/config/section/infra/systemVms'
@ -51,6 +52,7 @@ export default {
hosts,
primaryStorages,
secondaryStorages,
secondaryStagingStorages,
backupRepositories,
objectStorages,
systemVms,

View File

@ -0,0 +1,64 @@
// 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.
import store from '@/store'
export default {
name: 'imagecachestore',
title: 'label.secondary.staging.storage',
icon: 'file-image-outlined',
docHelp: 'adminguide/storage.html#secondary-storage',
permission: ['listSecondaryStagingStores'],
searchFilters: ['name', 'zoneid', 'provider'],
hidden: true,
columns: () => {
var fields = ['name', 'url', 'protocol', 'scope', 'zonename']
if (store.getters.apis.listSecondaryStagingStores.params.filter(x => x.name === 'readonly').length > 0) {
fields.push({
field: 'readonly',
customTitle: 'access'
})
}
return fields
},
details: () => {
var fields = ['name', 'id', 'url', 'protocol', 'provider', 'scope', 'zonename']
if (store.getters.apis.listSecondaryStagingStores.params.filter(x => x.name === 'readonly').length > 0) {
fields.push('readonly')
}
return fields
},
resourceType: 'SecondaryStagingStorage',
actions: [
{
api: 'createSecondaryStagingStore',
icon: 'plus-outlined',
docHelp: 'installguide/configuration.html#add-secondary-storage',
label: 'label.add.secondary.staging.storage',
listView: true,
args: ['url', 'zoneid', 'scope', 'provider']
},
{
api: 'deleteSecondaryStagingStore',
icon: 'delete-outlined',
label: 'label.action.delete.secondary.staging.storage',
message: 'message.action.delete.secondary.staging.storage',
dataView: true,
displayName: (record) => { return record.name || record.displayName || record.id }
}
]
}

View File

@ -82,6 +82,7 @@ import {
EyeOutlined,
FieldTimeOutlined,
FileDoneOutlined,
FileImageOutlined,
FileProtectOutlined,
FileSyncOutlined,
FileTextOutlined,
@ -257,6 +258,7 @@ export default {
app.component('EyeOutlined', EyeOutlined)
app.component('FieldTimeOutlined', FieldTimeOutlined)
app.component('FileDoneOutlined', FileDoneOutlined)
app.component('FileImageOutlined', FileImageOutlined)
app.component('FileProtectOutlined', FileProtectOutlined)
app.component('FileSyncOutlined', FileSyncOutlined)
app.component('FileTextOutlined', FileTextOutlined)

View File

@ -123,3 +123,16 @@ export function isValidIPv4Cidr (rule, value) {
resolve()
})
}
export function getSingularIfPluralWord(word) {
if (word.endsWith('ies') && word.length > 3) {
return word.slice(0, -3) + 'y'
}
if (word.endsWith('sses') || word.endsWith('shes') || word.endsWith('ches') || word.endsWith('xes') || word.endsWith('zes')) {
return word.slice(0, -2)
}
if (word.endsWith('s') && !word.endsWith('ss')) {
return word.slice(0, -1)
}
return word
}

View File

@ -156,8 +156,8 @@
v-if="routes[section]">
<chart-card :loading="loading">
<div class="chart-card-inner">
<router-link :to="{ name: section === 'backuprepositories' ? 'backuprepository' : section.substring(0, section.length - 1) }">
<h2>{{ $t(routes[section].title) }}</h2>
<router-link :to="{ name: routes[section].routename }">
<h2 style="overflow: hidden; text-overflow: ellipsis;">{{ $t(routes[section].title) }}</h2>
<h2><render-icon :icon="routes[section].icon" /> {{ stats[section] }}</h2>
</router-link>
</div>
@ -175,6 +175,7 @@ import router from '@/router'
import Breadcrumb from '@/components/widgets/Breadcrumb'
import ChartCard from '@/components/widgets/ChartCard'
import TooltipLabel from '@/components/widgets/TooltipLabel'
import { getSingularIfPluralWord } from '@/utils/util.js'
export default {
name: 'InfraSummary',
@ -187,7 +188,7 @@ export default {
return {
loading: true,
routes: {},
sections: ['zones', 'pods', 'clusters', 'hosts', 'storagepools', 'imagestores', 'backuprepositories', 'objectstores', 'systemvms', 'routers', 'cpusockets', 'managementservers', 'alerts', 'ilbvms', 'metrics'],
sections: ['zones', 'pods', 'clusters', 'hosts', 'storagepools', 'imagestores', 'imagecachestores', 'backuprepositories', 'objectstores', 'systemvms', 'routers', 'cpusockets', 'managementservers', 'alerts', 'ilbvms', 'metrics'],
sslFormVisible: false,
stats: {},
intermediateCertificates: [],
@ -216,12 +217,14 @@ export default {
fetchData () {
this.routes = {}
for (const section of this.sections) {
const route = section === 'backuprepositories' ? 'backuprepository' : section.substring(0, section.length - 1)
if (router.resolve('/' + route).matched[0].redirect === '/exception/404') {
const sectionRouteName = getSingularIfPluralWord(section)
const sectionKey = section
if (router.resolve('/' + sectionRouteName).matched[0].redirect === '/exception/404') {
continue
}
const node = router.resolve({ name: route })
this.routes[section] = {
const node = router.resolve({ name: sectionRouteName })
this.routes[sectionKey] = {
routename: sectionRouteName,
title: node.meta.title,
icon: node.meta.icon
}