Skip to content

Commit 2ab4e7f

Browse files
winterhazeldhslove
authored andcommitted
Fix link to removed volumes being shown in info card and list view (apache#8833)
* Framework for validating links in the front-end * Rename valid links map in the list view
1 parent 8e596d4 commit 2ab4e7f

9 files changed

Lines changed: 77 additions & 3 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,7 @@ public class ApiConstants {
595595
public static final String AGGREGATE_NAME = "aggregatename";
596596
public static final String POOL_NAME = "poolname";
597597
public static final String VOLUME_NAME = "volumename";
598+
public static final String VOLUME_STATE = "volumestate";
598599
public static final String SNAPSHOT_POLICY = "snapshotpolicy";
599600
public static final String SNAPSHOT_RESERVATION = "snapshotreservation";
600601
public static final String IP_NETWORK_LIST = "iptonetworklist";

api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements
7171
@Param(description = "type of the disk volume")
7272
private String volumeType;
7373

74+
@SerializedName(ApiConstants.VOLUME_STATE)
75+
@Param(description = "state of the disk volume")
76+
private String volumeState;
77+
7478
@SerializedName(ApiConstants.CREATED)
7579
@Param(description = " the date the snapshot was created")
7680
private Date created;
@@ -199,6 +203,10 @@ public void setVolumeType(String volumeType) {
199203
this.volumeType = volumeType;
200204
}
201205

206+
public void setVolumeState(String volumeState) {
207+
this.volumeState = volumeState;
208+
}
209+
202210
public void setCreated(Date created) {
203211
this.created = created;
204212
}

engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ SELECT
4848
`volumes`.`uuid` AS `volume_uuid`,
4949
`volumes`.`name` AS `volume_name`,
5050
`volumes`.`volume_type` AS `volume_type`,
51+
`volumes`.`state` AS `volume_state`,
5152
`volumes`.`size` AS `volume_size`,
5253
`data_center`.`id` AS `data_center_id`,
5354
`data_center`.`uuid` AS `data_center_uuid`,

server/src/main/java/com/cloud/api/ApiResponseHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ public SnapshotResponse createSnapshotResponse(Snapshot snapshot) {
670670
snapshotResponse.setVolumeId(volume.getUuid());
671671
snapshotResponse.setVolumeName(volume.getName());
672672
snapshotResponse.setVolumeType(volume.getVolumeType().name());
673+
snapshotResponse.setVolumeState(volume.getState().name());
673674
snapshotResponse.setVirtualSize(volume.getSize());
674675
DataCenter zone = ApiDBUtils.findZoneById(volume.getDataCenterId());
675676
if (zone != null) {

server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public SnapshotResponse newSnapshotResponse(ResponseObject.ResponseView view, bo
125125
snapshotResponse.setVolumeId(snapshot.getVolumeUuid());
126126
snapshotResponse.setVolumeName(snapshot.getVolumeName());
127127
snapshotResponse.setVolumeType(snapshot.getVolumeType().name());
128+
snapshotResponse.setVolumeState(snapshot.getVolumeState().name());
128129
snapshotResponse.setVirtualSize(snapshot.getVolumeSize());
129130
VolumeVO volume = ApiDBUtils.findVolumeById(snapshot.getVolumeId());
130131
if (volume != null && volume.getVolumeType() == Type.ROOT && volume.getInstanceId() != null) {

server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ public class SnapshotJoinVO extends BaseViewWithTagInformationVO implements Cont
132132
@Enumerated(EnumType.STRING)
133133
Volume.Type volumeType = Volume.Type.UNKNOWN;
134134

135+
@Column(name = "volume_state")
136+
@Enumerated(EnumType.STRING)
137+
Volume.State volumeState;
138+
135139
@Column(name = "volume_size")
136140
Long volumeSize;
137141

@@ -299,6 +303,10 @@ public Volume.Type getVolumeType() {
299303
return volumeType;
300304
}
301305

306+
public Volume.State getVolumeState() {
307+
return volumeState;
308+
}
309+
302310
public Long getVolumeSize() {
303311
return volumeSize;
304312
}

ui/src/components/view/InfoCard.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,8 @@
457457
<div class="resource-detail-item__label">{{ $t('label.volume') }}</div>
458458
<div class="resource-detail-item__details">
459459
<hdd-outlined />
460-
<router-link :to="{ path: '/volume/' + resource.volumeid }">{{ resource.volumename || resource.volume || resource.volumeid }} </router-link>
460+
<router-link v-if="validLinks.volume" :to="{ path: '/volume/' + resource.volumeid }">{{ resource.volumename || resource.volume || resource.volumeid }} </router-link>
461+
<span v-else>{{ resource.volumename || resource.volume || resource.volumeid }}</span>
461462
</div>
462463
</div>
463464
<div class="resource-detail-item" v-if="resource.associatednetworkid">
@@ -841,6 +842,7 @@
841842
<script>
842843
import { api } from '@/api'
843844
import { createPathBasedOnVmType } from '@/utils/plugins'
845+
import { validateLinks } from '@/utils/links'
844846
import Console from '@/components/widgets/Console'
845847
import OsLogo from '@/components/widgets/OsLogo'
846848
import Status from '@/components/widgets/Status'
@@ -907,7 +909,8 @@ export default {
907909
vpc: '',
908910
network: ''
909911
},
910-
newResource: {}
912+
newResource: {},
913+
validLinks: {}
911914
}
912915
},
913916
watch: {
@@ -924,6 +927,7 @@ export default {
924927
this.newResource = newData
925928
this.showKeys = false
926929
this.setData()
930+
this.validLinks = validateLinks(this.$router, this.isStatic, this.resource)
927931
928932
if ('apikey' in this.resource) {
929933
this.getUserKeys()

ui/src/components/view/ListView.vue

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@
170170
<router-link :to="{ path: getVmRouteUsingType(record) + record.virtualmachineid }">{{ text }}</router-link>
171171
</template>
172172
<template v-if="column.key === 'volumename'">
173-
<router-link :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
173+
<router-link v-if="resourceIdToValidLinksMap[record.id]?.volume" :to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
174+
<span v-else>{{ text }}</span>
174175
</template>
175176
<template v-if="column.key === 'size'">
176177
<span v-if="text && $route.path === '/kubernetes'">
@@ -530,6 +531,7 @@ import ResourceLabel from '@/components/widgets/ResourceLabel'
530531
import Status from '@/components/widgets/Status'
531532
import TooltipButton from '@/components/widgets/TooltipButton'
532533
import { createPathBasedOnVmType } from '@/utils/plugins'
534+
import { validateLinks } from '@/utils/links'
533535
import cronstrue from 'cronstrue/i18n'
534536
import moment from 'moment-timezone'
535537
@@ -622,6 +624,18 @@ export default {
622624
notification: 'storageallocatedthreshold',
623625
disable: 'storageallocateddisablethreshold'
624626
}
627+
},
628+
resourceIdToValidLinksMap: {}
629+
}
630+
},
631+
watch: {
632+
items: {
633+
deep: true,
634+
handler (newData, oldData) {
635+
if (newData === oldData) return
636+
this.items.forEach(record => {
637+
this.resourceIdToValidLinksMap[record.id] = validateLinks(this.$router, false, record)
638+
})
625639
}
626640
}
627641
},

ui/src/utils/links.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
export function validateLinks (router, isStatic, resource) {
19+
const validLinks = {
20+
volume: false
21+
}
22+
23+
if (isStatic) {
24+
return validLinks
25+
}
26+
27+
if (resource.volumeid && router.resolve('/volume/' + resource.volumeid).matched[0].redirect !== '/exception/404') {
28+
if (resource.volumestate) {
29+
validLinks.volume = resource.volumestate !== 'Expunged'
30+
} else {
31+
validLinks.volume = true
32+
}
33+
}
34+
35+
return validLinks
36+
}

0 commit comments

Comments
 (0)