This is an automated email from the ASF dual-hosted git repository. shwstppr pushed a commit to branch 4.22 in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 031fbf43d4f5719287d37d3a8e11e210e112211a Merge: 2399edd3807 8627c60b951 Author: Abhishek Kumar <[email protected]> AuthorDate: Tue Jan 13 11:48:05 2026 +0530 Merge remote-tracking branch 'apache/4.20' into 4.22 plugins/hypervisors/kvm/pom.xml | 25 +++++ .../driver/CephObjectStoreDriverImpl.java | 2 +- .../driver/LinstorPrimaryDataStoreDriverImpl.java | 101 +++++++++++---------- pom.xml | 12 +++ ui/public/locales/en.json | 2 + ui/src/components/view/InfoCard.vue | 2 +- .../InstanceVolumesStoragePoolSelectListView.vue | 12 ++- ui/src/components/view/ListView.vue | 19 +--- .../view/VolumeStoragePoolSelectForm.vue | 16 +++- ui/src/components/view/VolumesTab.vue | 2 +- ui/src/components/widgets/Breadcrumb.vue | 2 +- ui/src/config/router.js | 4 +- ui/src/store/modules/user.js | 19 ++-- ui/src/views/compute/MigrateWizard.vue | 60 ++++++++++-- ui/src/views/image/IsoZones.vue | 2 +- ui/src/views/image/TemplateZones.vue | 2 +- ui/src/views/storage/SnapshotZones.vue | 2 +- ui/tests/unit/views/compute/MigrateWizard.spec.js | 52 +++++------ utils/pom.xml | 5 + 19 files changed, 221 insertions(+), 120 deletions(-) diff --cc ui/public/locales/en.json index 5667fc33484,791091e8e2a..bda74027288 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@@ -4033,7 -3693,7 +4034,8 @@@ "message.vnf.nic.move.down.fail": "Failed to move down this NIC", "message.vnf.no.credentials": "No credentials found for the VNF appliance.", "message.vnf.select.networks": "Please select the relevant network for each VNF NIC.", +"message.volume.desc": "Volume to use as a ROOT disk", + "message.volume.pool.apply.to.all": "Selected storage pool will be applied to all existing volumes of the instance.", "message.volume.state.allocated": "The volume is allocated but has not been created yet.", "message.volume.state.attaching": "The volume is attaching to a volume from Ready state.", "message.volume.state.copying": "The volume is being copied from the image store to primary storage, in case it's an uploaded volume.", diff --cc ui/src/components/view/ListView.vue index 47aa3d2ddef,a02fb5569ed..168e355cbc8 --- a/ui/src/components/view/ListView.vue +++ b/ui/src/components/view/ListView.vue @@@ -115,63 -78,25 +115,55 @@@ </span> <span v-else-if="$route.path.startsWith('/globalsetting')">{{ text }}</span> <span v-else-if="$route.path.startsWith('/alert')"> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.id }" + v-if="record.id" + >{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.name }" + v-else + >{{ $t(text.toLowerCase()) }}</router-link> </span> <span v-else-if="$route.path.startsWith('/tungstenfabric')"> - <router-link :to="{ path: $route.path + '/' + record.id }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.id }" + v-if="record.id" + >{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.name }" + v-else + >{{ $t(text.toLowerCase()) }}</router-link> </span> <span v-else-if="isTungstenPath()"> - <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" v-if="record.uuid && record.zoneid">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" v-else-if="record.uuid && $route.query.zoneid">{{ $t(text.toLowerCase()) }}</router-link> - <router-link :to="{ path: $route.path }" v-else>{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: record.zoneid } }" + v-if="record.uuid && record.zoneid" + >{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.uuid, query: { zoneid: $route.query.zoneid } }" + v-else-if="record.uuid && $route.query.zoneid" + >{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path }" + v-else + >{{ $t(text.toLowerCase()) }}</router-link> </span> <span v-else-if="$route.path.startsWith('/guestnetwork') && record.id && record.displaynetwork === false"> - <router-link :to="{ path: $route.path + '/' + record.id, query: { displaynetwork: false } }" v-if="record.id">{{ $t(text.toLowerCase()) }}</router-link> + <router-link + :to="{ path: $route.path + '/' + record.id, query: { displaynetwork: false } }" + v-if="record.id" + >{{ $t(text.toLowerCase()) }}</router-link> + </span> + <span v-else-if="$route.path.startsWith('/gpucard') && record.gpucardid"> + <router-link + :to="{ path: '/vgpuprofile/' + record.id }" + v-if="record.id" + >{{ $t(text.toLowerCase()) }}</router-link> </span> <span v-else> - <router-link - :to="{ path: $route.path + '/' + record.id }" - v-if="record.id" - >{{ text }}</router-link> - <router-link - :to="{ path: $route.path + '/' + record.name }" - v-else - >{{ text }}</router-link> - <span - v-if="['guestnetwork','vpc'].includes($route.path.split('/')[1]) && record.restartrequired && !record.vpcid" - > + <router-link :to="{ path: $route.path + '/' + encodeURIComponent(record.id) }" v-if="record.id">{{ text }}</router-link> + <router-link :to="{ path: $route.path + '/' + record.name }" v-else>{{ text }}</router-link> + <span v-if="['guestnetwork','vpc'].includes($route.path.split('/')[1]) && record.restartrequired && !record.vpcid"> <a-tooltip> <template #title>{{ $t('label.restartrequired') }}</template> @@@ -607,34 -306,19 +599,31 @@@ <span v-else>{{ text }}</span> </template> <template v-if="column.key === 'storage'"> - <router-link - v-if="record.storageid" - :to="{ path: '/storagepool/' + record.storageid }" - >{{ text }}</router-link> + <router-link v-if="record.storageid" :to="{ path: '/storagepool/' + encodeURIComponent(record.storageid) }">{{ text }}</router-link> <span v-else>{{ text }}</span> </template> - <template v-for="(value, name) in thresholdMapping" :key="name"> + <template + v-for="(value, name) in thresholdMapping" + :key="name" + > <template v-if="column.key === name"> <span> - <span v-if="record[value.disable]" class="alert-disable-threshold"> + <span + v-if="record[value.disable]" + class="alert-disable-threshold" + > {{ text }} </span> - <span v-else-if="record[value.notification]" class="alert-notification-threshold"> + <span + v-else-if="record[value.notification]" + class="alert-notification-threshold" + > {{ text }} </span> - <span style="padding: 10%;" v-else> + <span + style="padding: 10%;" + v-else + > {{ text }} </span> </span> diff --cc ui/src/store/modules/user.js index 2c0edf656d7,21cd603e378..5c78b8a592f --- a/ui/src/store/modules/user.js +++ b/ui/src/store/modules/user.js @@@ -562,15 -538,17 +562,16 @@@ const user = }).catch(error => { reject(error) }) -- - getAPI('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { - if (json.listconfigurationsresponse.configuration !== null) { - const config = json.listconfigurationsresponse.configuration[0] - commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) - } - }).catch(error => { - reject(error) - }) + if ('listConfigurations' in store.getters.apis) { - api('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { ++ getAPI('listConfigurations', { name: 'hypervisor.custom.display.name' }).then(json => { + if (json.listconfigurationsresponse.configuration !== null) { + const config = json.listconfigurationsresponse.configuration[0] + commit('SET_CUSTOM_HYPERVISOR_NAME', config.value) + } + }).catch(error => { + reject(error) + }) + } }) }, UpdateConfiguration ({ commit }) { diff --cc ui/src/views/compute/MigrateWizard.vue index f70a08e8006,eee29845ead..ec756da3037 --- a/ui/src/views/compute/MigrateWizard.vue +++ b/ui/src/views/compute/MigrateWizard.vue @@@ -124,8 -144,9 +144,9 @@@ </template> <script> -import { api } from '@/api' +import { getAPI, postAPI } from '@/api' import TooltipLabel from '@/components/widgets/TooltipLabel' + import StoragePoolSelectView from '@/components/view/StoragePoolSelectView' import InstanceVolumesStoragePoolSelectListView from '@/components/view/InstanceVolumesStoragePoolSelectListView' export default { @@@ -212,8 -238,12 +238,12 @@@ return array !== null && array !== undefined && Array.isArray(array) && array.length > 0 }, fetchData () { + this.fetchHostsForMigration() + this.fetchVolumes() + }, + fetchHostsForMigration () { this.loading = true - api('findHostsForMigration', { + getAPI('findHostsForMigration', { virtualmachineid: this.resource.id, keyword: this.searchQuery, page: this.page, diff --cc ui/tests/unit/views/compute/MigrateWizard.spec.js index b847551f423,2404fda6c8c..e6434644ee6 --- a/ui/tests/unit/views/compute/MigrateWizard.spec.js +++ b/ui/tests/unit/views/compute/MigrateWizard.spec.js @@@ -301,11 -305,12 +301,11 @@@ describe('Views > compute > MigrateWiza await mockAxios.mockRejectedValue(mockError) await wrapper.setProps({ resource: {} }) - await wrapper.vm.fetchData() + await wrapper.vm.fetchHostsForMigration() await flushPromises() - await flushPromises() - expect(mocks.$message.error).toHaveBeenCalled() - expect(mocks.$message.error).toHaveBeenLastCalledWith(`${i18n.global.t('message.load.host.failed')}: ${mockError}`) + expect(mocks.$notifyError).toHaveBeenCalled() + expect(mocks.$notifyError).toHaveBeenLastCalledWith(mockError) done() }) })
