Skip to content

Commit 973819d

Browse files
authored
API: Add support to list all snapshot policies & backup schedules (#11587)
* API: Add support to list all snapshot policies & backup schedules * Add support for backup policy listing without tying it to the vmid * add tests for snapshot policy listing * update tests for listbackupschedules * remove trailing spaces and fix lint failure * Add upgrade test * remove unused import * add create policy - snap/backup in the list view with resource (volume/vm) selection * add translations * refresh parent list * remove unnecessary alert info * fix checks for UI backup schedule list view * fix checks for UI backup schedule list view * add back access checks * add since param * fix failing test * update snapshot policy and backup schedule ownership when VM is moved * fix issue with showing vm selection * fix unit test failure * Update list snappolicy & backup schedule logic to list only those that belong to a proj or for root admin those that belong to it, unless listall & projid is passed * fix test * support snap / backup policy search using keyword * fix tests
1 parent f67b738 commit 973819d

File tree

31 files changed

+1553
-73
lines changed

31 files changed

+1553
-73
lines changed

api/src/main/java/com/cloud/storage/snapshot/SnapshotApiService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public interface SnapshotApiService {
8585
* the command that specifies the volume criteria
8686
* @return list of snapshot policies
8787
*/
88-
Pair<List<? extends SnapshotPolicy>, Integer> listPoliciesforVolume(ListSnapshotPoliciesCmd cmd);
88+
Pair<List<? extends SnapshotPolicy>, Integer> listSnapshotPolicies(ListSnapshotPoliciesCmd cmd);
8989

9090
boolean deleteSnapshotPolicies(DeleteSnapshotPoliciesCmd cmd);
9191

api/src/main/java/com/cloud/storage/snapshot/SnapshotPolicy.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
// under the License.
1717
package com.cloud.storage.snapshot;
1818

19+
import org.apache.cloudstack.acl.ControlledEntity;
1920
import org.apache.cloudstack.api.Displayable;
2021
import org.apache.cloudstack.api.Identity;
2122
import org.apache.cloudstack.api.InternalIdentity;
2223

23-
public interface SnapshotPolicy extends Identity, InternalIdentity, Displayable {
24+
public interface SnapshotPolicy extends ControlledEntity, Identity, InternalIdentity, Displayable {
2425

2526
long getVolumeId();
2627

api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupScheduleCmd.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.apache.cloudstack.api.APICommand;
2525
import org.apache.cloudstack.api.ApiConstants;
2626
import org.apache.cloudstack.api.ApiErrorCode;
27-
import org.apache.cloudstack.api.BaseCmd;
27+
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
2828
import org.apache.cloudstack.api.Parameter;
2929
import org.apache.cloudstack.api.ServerApiException;
3030
import org.apache.cloudstack.api.response.BackupScheduleResponse;
@@ -39,7 +39,6 @@
3939
import com.cloud.exception.NetworkRuleConflictException;
4040
import com.cloud.exception.ResourceAllocationException;
4141
import com.cloud.exception.ResourceUnavailableException;
42-
import com.cloud.utils.exception.CloudRuntimeException;
4342

4443
import java.util.ArrayList;
4544
import java.util.List;
@@ -48,10 +47,10 @@
4847
description = "List backup schedule of a VM",
4948
responseObject = BackupScheduleResponse.class, since = "4.14.0",
5049
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
51-
public class ListBackupScheduleCmd extends BaseCmd {
50+
public class ListBackupScheduleCmd extends BaseListProjectAndAccountResourcesCmd {
5251

5352
@Inject
54-
private BackupManager backupManager;
53+
BackupManager backupManager;
5554

5655
/////////////////////////////////////////////////////
5756
//////////////// API parameters /////////////////////
@@ -60,10 +59,16 @@ public class ListBackupScheduleCmd extends BaseCmd {
6059
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
6160
type = CommandType.UUID,
6261
entityType = UserVmResponse.class,
63-
required = true,
6462
description = "ID of the VM")
6563
private Long vmId;
6664

65+
@Parameter(name = ApiConstants.ID,
66+
type = CommandType.UUID,
67+
entityType = BackupScheduleResponse.class,
68+
description = "the ID of the backup schedule",
69+
since = "4.22.0")
70+
private Long id;
71+
6772
/////////////////////////////////////////////////////
6873
/////////////////// Accessors ///////////////////////
6974
/////////////////////////////////////////////////////
@@ -72,26 +77,29 @@ public Long getVmId() {
7277
return vmId;
7378
}
7479

80+
public Long getId() {
81+
return id;
82+
}
83+
7584
/////////////////////////////////////////////////////
7685
/////////////// API Implementation///////////////////
7786
/////////////////////////////////////////////////////
7887

7988
@Override
8089
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
8190
try{
82-
List<BackupSchedule> schedules = backupManager.listBackupSchedule(getVmId());
91+
List<BackupSchedule> schedules = backupManager.listBackupSchedules(this);
8392
ListResponse<BackupScheduleResponse> response = new ListResponse<>();
8493
List<BackupScheduleResponse> scheduleResponses = new ArrayList<>();
94+
8595
if (!CollectionUtils.isNullOrEmpty(schedules)) {
8696
for (BackupSchedule schedule : schedules) {
8797
scheduleResponses.add(_responseGenerator.createBackupScheduleResponse(schedule));
8898
}
89-
response.setResponses(scheduleResponses, schedules.size());
90-
response.setResponseName(getCommandName());
91-
setResponseObject(response);
92-
} else {
93-
throw new CloudRuntimeException("No backup schedule exists for the VM");
9499
}
100+
response.setResponses(scheduleResponses, schedules.size());
101+
response.setResponseName(getCommandName());
102+
setResponseObject(response);
95103
} catch (Exception e) {
96104
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
97105
}

api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/ListSnapshotPoliciesCmd.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
import org.apache.cloudstack.api.APICommand;
2525
import org.apache.cloudstack.api.ApiConstants;
26-
import org.apache.cloudstack.api.BaseListCmd;
26+
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
2727
import org.apache.cloudstack.api.Parameter;
2828
import org.apache.cloudstack.api.response.ListResponse;
2929
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
@@ -34,7 +34,7 @@
3434

3535
@APICommand(name = "listSnapshotPolicies", description = "Lists snapshot policies.", responseObject = SnapshotPolicyResponse.class,
3636
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
37-
public class ListSnapshotPoliciesCmd extends BaseListCmd {
37+
public class ListSnapshotPoliciesCmd extends BaseListProjectAndAccountResourcesCmd {
3838

3939

4040
/////////////////////////////////////////////////////
@@ -69,13 +69,14 @@ public boolean isDisplay() {
6969
public Long getId() {
7070
return id;
7171
}
72+
7273
/////////////////////////////////////////////////////
7374
/////////////// API Implementation///////////////////
7475
/////////////////////////////////////////////////////
7576

7677
@Override
7778
public void execute() {
78-
Pair<List<? extends SnapshotPolicy>, Integer> result = _snapshotService.listPoliciesforVolume(this);
79+
Pair<List<? extends SnapshotPolicy>, Integer> result = _snapshotService.listSnapshotPolicies(this);
7980
ListResponse<SnapshotPolicyResponse> response = new ListResponse<SnapshotPolicyResponse>();
8081
List<SnapshotPolicyResponse> policyResponses = new ArrayList<SnapshotPolicyResponse>();
8182
for (SnapshotPolicy policy : result.first()) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public class SnapshotPolicyResponse extends BaseResponseWithTagInformation {
3737
@Param(description = "the ID of the disk volume")
3838
private String volumeId;
3939

40+
@SerializedName("volumename")
41+
@Param(description = "the name of the disk volume")
42+
private String volumeName;
43+
4044
@SerializedName("schedule")
4145
@Param(description = "time the snapshot is scheduled to be taken.")
4246
private String schedule;
@@ -87,6 +91,10 @@ public void setVolumeId(String volumeId) {
8791
this.volumeId = volumeId;
8892
}
8993

94+
public void setVolumeName(String volumeName) {
95+
this.volumeName = volumeName;
96+
}
97+
9098
public String getSchedule() {
9199
return schedule;
92100
}

api/src/main/java/org/apache/cloudstack/backup/BackupManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
2929
import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd;
3030
import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
31+
import org.apache.cloudstack.api.command.user.backup.ListBackupScheduleCmd;
3132
import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
3233
import org.apache.cloudstack.api.response.BackupResponse;
3334
import org.apache.cloudstack.framework.config.ConfigKey;
@@ -174,7 +175,7 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
174175
* @param vmId
175176
* @return
176177
*/
177-
List<BackupSchedule> listBackupSchedule(Long vmId);
178+
List<BackupSchedule> listBackupSchedules(ListBackupScheduleCmd cmd);
178179

179180
/**
180181
* Deletes VM backup schedule for a VM

api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919

2020
import java.util.Date;
2121

22+
import org.apache.cloudstack.acl.ControlledEntity;
2223
import org.apache.cloudstack.api.InternalIdentity;
2324

2425
import com.cloud.utils.DateUtil;
2526

26-
public interface BackupSchedule extends InternalIdentity {
27+
public interface BackupSchedule extends ControlledEntity, InternalIdentity {
2728
Long getVmId();
2829
DateUtil.IntervalType getScheduleType();
2930
String getSchedule();
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
package org.apache.cloudstack.api.command.user.backup;
18+
19+
import com.cloud.exception.InsufficientCapacityException;
20+
import com.cloud.exception.NetworkRuleConflictException;
21+
import com.cloud.exception.ResourceAllocationException;
22+
import com.cloud.exception.ResourceUnavailableException;
23+
import com.cloud.user.Account;
24+
import org.apache.cloudstack.api.ResponseGenerator;
25+
import org.apache.cloudstack.api.response.BackupScheduleResponse;
26+
import org.apache.cloudstack.api.response.ListResponse;
27+
import org.apache.cloudstack.backup.BackupManager;
28+
import org.apache.cloudstack.backup.BackupSchedule;
29+
import org.apache.cloudstack.context.CallContext;
30+
import org.junit.Assert;
31+
import org.junit.Before;
32+
import org.junit.Test;
33+
import org.junit.runner.RunWith;
34+
import org.mockito.Mock;
35+
import org.mockito.Mockito;
36+
import org.mockito.junit.MockitoJUnitRunner;
37+
38+
import java.util.ArrayList;
39+
import java.util.List;
40+
41+
@RunWith(MockitoJUnitRunner.class)
42+
public class ListBackupScheduleCmdTest {
43+
44+
@Mock
45+
private BackupManager backupManager;
46+
47+
@Mock
48+
private ResponseGenerator responseGenerator;
49+
50+
private ListBackupScheduleCmd cmd;
51+
52+
@Before
53+
public void setUp() {
54+
cmd = new ListBackupScheduleCmd();
55+
cmd.backupManager = backupManager;
56+
cmd._responseGenerator = responseGenerator;
57+
}
58+
59+
@Test
60+
public void testExecuteWithSchedules() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, NetworkRuleConflictException {
61+
BackupSchedule schedule = Mockito.mock(BackupSchedule.class);
62+
BackupScheduleResponse scheduleResponse = Mockito.mock(BackupScheduleResponse.class);
63+
List<BackupSchedule> schedules = new ArrayList<>();
64+
schedules.add(schedule);
65+
66+
Mockito.when(backupManager.listBackupSchedules(cmd)).thenReturn(schedules);
67+
Mockito.when(responseGenerator.createBackupScheduleResponse(schedule)).thenReturn(scheduleResponse);
68+
69+
Account mockAccount = Mockito.mock(Account.class);
70+
CallContext callContext = Mockito.mock(CallContext.class);
71+
try (org.mockito.MockedStatic<CallContext> mocked = Mockito.mockStatic(CallContext.class)) {
72+
cmd.execute();
73+
}
74+
75+
ListResponse<?> response = (ListResponse<?>) cmd.getResponseObject();
76+
Assert.assertNotNull(response);
77+
Assert.assertEquals(1, response.getResponses().size());
78+
Assert.assertEquals(scheduleResponse, response.getResponses().get(0));
79+
}
80+
81+
@Test
82+
public void testExecuteWithNoSchedules() {
83+
Mockito.when(backupManager.listBackupSchedules(cmd)).thenReturn(new ArrayList<>());
84+
CallContext callContext = Mockito.mock(CallContext.class);
85+
86+
try (org.mockito.MockedStatic<CallContext> mocked = Mockito.mockStatic(CallContext.class)) {
87+
mocked.when(CallContext::current).thenReturn(callContext);
88+
cmd.execute();
89+
} catch (ResourceUnavailableException | InsufficientCapacityException | ResourceAllocationException |
90+
NetworkRuleConflictException e) {
91+
throw new RuntimeException(e);
92+
}
93+
94+
ListResponse<?> response = (ListResponse<?>) cmd.getResponseObject();
95+
Assert.assertNotNull(response);
96+
Assert.assertEquals(0, response.getResponses().size());
97+
}
98+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
package org.apache.cloudstack.api.command.user.snapshot;
18+
19+
import com.cloud.storage.snapshot.SnapshotApiService;
20+
import com.cloud.storage.snapshot.SnapshotPolicy;
21+
import com.cloud.utils.Pair;
22+
import org.apache.cloudstack.api.ResponseGenerator;
23+
import org.apache.cloudstack.api.response.ListResponse;
24+
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
25+
import org.junit.Assert;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
import org.mockito.Mockito;
29+
30+
import java.util.ArrayList;
31+
import java.util.List;
32+
33+
public class ListSnapshotPoliciesCmdTest {
34+
private ListSnapshotPoliciesCmd cmd;
35+
private SnapshotApiService snapshotService;
36+
private ResponseGenerator responseGenerator;
37+
38+
@Before
39+
public void setUp() {
40+
cmd = new ListSnapshotPoliciesCmd();
41+
snapshotService = Mockito.mock(SnapshotApiService.class);
42+
responseGenerator = Mockito.mock(ResponseGenerator.class);
43+
44+
cmd._snapshotService = snapshotService;
45+
cmd._responseGenerator = responseGenerator;
46+
}
47+
48+
@Test
49+
public void testExecuteWithPolicies() {
50+
SnapshotPolicy policy = Mockito.mock(SnapshotPolicy.class);
51+
SnapshotPolicyResponse policyResponse = Mockito.mock(SnapshotPolicyResponse.class);
52+
List<SnapshotPolicy> policies = new ArrayList<>();
53+
policies.add(policy);
54+
55+
Mockito.when(snapshotService.listSnapshotPolicies(cmd))
56+
.thenReturn(new Pair<>(policies, 1));
57+
Mockito.when(responseGenerator.createSnapshotPolicyResponse(policy))
58+
.thenReturn(policyResponse);
59+
60+
cmd.execute();
61+
62+
ListResponse<?> response = (ListResponse<?>) cmd.getResponseObject();
63+
Assert.assertNotNull(response);
64+
Assert.assertEquals(1, response.getResponses().size());
65+
Assert.assertEquals(policyResponse, response.getResponses().get(0));
66+
}
67+
68+
@Test
69+
public void testExecuteWithNoPolicies() {
70+
Mockito.when(snapshotService.listSnapshotPolicies(cmd))
71+
.thenReturn(new Pair<>(new ArrayList<>(), 0));
72+
73+
cmd.execute();
74+
75+
ListResponse<?> response = (ListResponse<?>) cmd.getResponseObject();
76+
Assert.assertNotNull(response);
77+
Assert.assertTrue(response.getResponses().isEmpty());
78+
}
79+
}

0 commit comments

Comments
 (0)