This is an automated email from the ASF dual-hosted git repository.
shwstppr pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push:
new 8261beca2fb ui: fix info card showing invalid template, iso link
(#13199)
8261beca2fb is described below
commit 8261beca2fb4415015450f78fbf24c6c679abce3
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Sep 11 16:08:56 2026 +0530
ui: fix info card showing invalid template, iso link (#13199)
Template/ISO for a VM could be in deleted state therefore links should not
be shown for them in the VM info-card.
Signed-off-by: Abhishek Kumar <[email protected]>
---
ui/src/components/view/InfoCard.vue | 12 ++++---
ui/src/utils/links.js | 72 +++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+), 5 deletions(-)
diff --git a/ui/src/components/view/InfoCard.vue
b/ui/src/components/view/InfoCard.vue
index 0272df028a3..34c29923123 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -560,7 +560,8 @@
<div class="resource-detail-item__details">
<resource-icon v-if="resource.icon"
:image="getImage(resource.icon.base64image)" size="1x" style="margin-right:
5px"/>
<SaveOutlined v-else />
- <router-link :to="{ path: (resource.templateformat === 'ISO' ?
'/iso/' : '/template/') + resource.templateid }">{{
resource.templatedisplaytext || resource.templatename || resource.templateid }}
</router-link>
+ <router-link v-if="validLinks.template" :to="{ path:
(resource.templateformat === 'ISO' ? '/iso/' : '/template/') +
resource.templateid }">{{ resource.templatedisplaytext || resource.templatename
|| resource.templateid }} </router-link>
+ <span v-else>{{ resource.templatedisplaytext ||
resource.templatename || resource.templateid }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="resource.isoid">
@@ -568,7 +569,8 @@
<div class="resource-detail-item__details">
<resource-icon v-if="resource.icon"
:image="getImage(resource.icon.base64image)" size="1x" style="margin-right:
5px"/>
<UsbOutlined v-else />
- <router-link :to="{ path: '/iso/' + resource.isoid }">{{
resource.isodisplaytext || resource.isoname || resource.isoid }} </router-link>
+ <router-link v-if="validLinks.iso" :to="{ path: '/iso/' +
resource.isoid }">{{ resource.isodisplaytext || resource.isoname ||
resource.isoid }} </router-link>
+ <span v-else>{{ resource.isodisplaytext || resource.isoname ||
resource.isoid }}</span>
</div>
</div>
<div class="resource-detail-item"
v-if="resource.serviceofferingname && resource.serviceofferingid">
@@ -881,7 +883,7 @@
<script>
import { api } from '@/api'
import { createPathBasedOnVmType } from '@/utils/plugins'
-import { validateLinks } from '@/utils/links'
+import { validateLinksAsync } from '@/utils/links'
import Console from '@/components/widgets/Console'
import OsLogo from '@/components/widgets/OsLogo'
import Status from '@/components/widgets/Status'
@@ -966,12 +968,12 @@ export default {
},
resource: {
deep: true,
- handler (newData, oldData) {
+ async handler (newData, oldData) {
if (newData === oldData) return
this.newResource = newData
this.showKeys = false
this.setData()
- this.validLinks = validateLinks(this.$router, this.isStatic,
this.resource)
+ this.validLinks = await validateLinksAsync(this.$router,
this.isStatic, this.resource)
if ('apikey' in this.resource) {
this.getUserKeys()
diff --git a/ui/src/utils/links.js b/ui/src/utils/links.js
index fa650cdd7b8..3524dcc8f8e 100644
--- a/ui/src/utils/links.js
+++ b/ui/src/utils/links.js
@@ -15,6 +15,27 @@
// specific language governing permissions and limitations
// under the License.
+import { api } from '@/api'
+
+async function isValidObject (apiName, id, params) {
+ try {
+ const allParams = { ...params, listAll: true, id }
+ const json = await api(apiName, allParams)
+ const responseName = Object.keys(json).find(key =>
key.endsWith('response')) || apiName.toLowerCase() + 'response'
+ const response = json?.[responseName]
+ if (!response) {
+ return false
+ }
+ const objectName = Object.keys(response).find(key => key !== 'count')
+ if (!objectName || !Array.isArray(response[objectName])) {
+ return false
+ }
+ return response[objectName].some(item => item.id === id)
+ } catch (e) {
+ return false
+ }
+}
+
export function validateLinks (router, isStatic, resource) {
const validLinks = {
volume: false
@@ -34,3 +55,54 @@ export function validateLinks (router, isStatic, resource) {
return validLinks
}
+
+export async function validateLinksAsync (router, isStatic, resource) {
+ const validLinks = {
+ volume: false,
+ template: false,
+ iso: false
+ }
+ const pendingChecks = []
+
+ if (isStatic) {
+ return validLinks
+ }
+
+ if (resource.volumeid && router.resolve('/volume/' +
resource.volumeid).matched[0].redirect !== '/exception/404') {
+ if (resource.volumestate) {
+ validLinks.volume = resource.volumestate !== 'Expunged'
+ } else {
+ validLinks.volume = true
+ }
+ }
+
+ if (resource.templateid) {
+ const templatePath = (resource.templateformat === 'ISO' ? '/iso/' :
'/template/') + resource.templateid
+ if (router.resolve(templatePath).matched[0].redirect !== '/exception/404')
{
+ pendingChecks.push(
+ isValidObject('listTemplates', resource.templateid, { templatefilter:
'executable' }).then(result => {
+ validLinks.template = result
+ })
+ )
+ }
+ }
+
+ if (resource.isoid) {
+ const isoPath = '/iso/' + resource.isoid
+ if (router.resolve(isoPath).matched[0].redirect !== '/exception/404') {
+ pendingChecks.push(
+ isValidObject('listIsos', resource.isoid, { isofilter: 'executable'
}).then(result => {
+ validLinks.iso = result
+ })
+ )
+ }
+ }
+
+ if (pendingChecks.length) {
+ await Promise.all(pendingChecks).catch(error => {
+ console.error('Error validating links:', error)
+ })
+ }
+
+ return validLinks
+}