Skip to content

Commit 965a47f

Browse files
SadiJrSadiJr
andauthored
Create UpdateBackupOffering API (apache#5511)
* Create UpdateBackupOffering API * Address reviews * Address reviews * Address reviews Co-authored-by: SadiJr <sadi@scclouds.com.br>
1 parent df0c004 commit 965a47f

6 files changed

Lines changed: 283 additions & 0 deletions

File tree

api/src/main/java/com/cloud/event/EventTypes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ public class EventTypes {
521521
public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE";
522522
public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE";
523523
public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC";
524+
public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT";
524525

525526
// external network device events
526527
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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.admin.backup;
18+
19+
import javax.inject.Inject;
20+
21+
import org.apache.cloudstack.api.APICommand;
22+
import org.apache.cloudstack.api.ApiConstants;
23+
import org.apache.cloudstack.api.ApiErrorCode;
24+
import org.apache.cloudstack.api.BaseCmd;
25+
import org.apache.cloudstack.api.Parameter;
26+
import org.apache.cloudstack.api.ServerApiException;
27+
import org.apache.cloudstack.api.response.BackupOfferingResponse;
28+
import org.apache.cloudstack.backup.BackupManager;
29+
import org.apache.cloudstack.backup.BackupOffering;
30+
import org.apache.commons.lang3.StringUtils;
31+
import org.apache.log4j.Logger;
32+
33+
import com.cloud.exception.InvalidParameterValueException;
34+
import com.cloud.user.Account;
35+
import com.cloud.utils.exception.CloudRuntimeException;
36+
37+
@APICommand(name = "updateBackupOffering", description = "Updates a backup offering.", responseObject = BackupOfferingResponse.class,
38+
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0")
39+
public class UpdateBackupOfferingCmd extends BaseCmd {
40+
private static final Logger LOGGER = Logger.getLogger(UpdateBackupOfferingCmd.class.getName());
41+
private static final String APINAME = "updateBackupOffering";
42+
43+
@Inject
44+
private BackupManager backupManager;
45+
46+
/////////////////////////////////////////////////////
47+
//////////////// API parameters /////////////////////
48+
/////////////////////////////////////////////////////
49+
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupOfferingResponse.class, required = true, description = "The ID of the Backup Offering to be updated")
50+
private Long id;
51+
52+
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the Backup Offering to be updated")
53+
private String description;
54+
55+
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Backup Offering to be updated")
56+
private String name;
57+
58+
/////////////////////////////////////////////////////
59+
/////////////////// Accessors ///////////////////////
60+
/////////////////////////////////////////////////////
61+
public Long getId() {
62+
return id;
63+
}
64+
65+
public String getName() {
66+
return name;
67+
}
68+
69+
public String getDescription() {
70+
return description;
71+
}
72+
73+
/////////////////////////////////////////////////////
74+
/////////////// API Implementation///////////////////
75+
/////////////////////////////////////////////////////
76+
@Override
77+
public void execute() {
78+
try {
79+
if (StringUtils.isAllEmpty(name, description)) {
80+
throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there is no change in name or description.", id));
81+
}
82+
83+
BackupOffering result = backupManager.updateBackupOffering(this);
84+
85+
if (result == null) {
86+
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to update backup offering [id: %s, name: %s, description: %s].", id, name, description));
87+
}
88+
BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(result);
89+
response.setResponseName(getCommandName());
90+
this.setResponseObject(response);
91+
} catch (CloudRuntimeException e) {
92+
ApiErrorCode paramError = e instanceof InvalidParameterValueException ? ApiErrorCode.PARAM_ERROR : ApiErrorCode.INTERNAL_ERROR;
93+
LOGGER.error(String.format("Failed to update Backup Offering [id: %s] due to: [%s].", id, e.getMessage()), e);
94+
throw new ServerApiException(paramError, e.getMessage());
95+
}
96+
}
97+
98+
@Override
99+
public String getCommandName() {
100+
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
101+
}
102+
103+
@Override
104+
public long getEntityOwnerId() {
105+
return Account.ACCOUNT_ID_SYSTEM;
106+
}
107+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121

2222
import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
23+
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
2324
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
2425
import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
2526
import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
@@ -137,4 +138,6 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
137138
* @return returns operation success
138139
*/
139140
boolean deleteBackup(final Long backupId);
141+
142+
BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd);
140143
}

engine/schema/src/main/java/org/apache/cloudstack/backup/BackupOfferingVO.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ public String getName() {
9393
return name;
9494
}
9595

96+
public void setName(String name) {
97+
this.name = name;
98+
}
99+
96100
public String getExternalId() {
97101
return externalId;
98102
}
@@ -120,6 +124,10 @@ public String getDescription() {
120124
return description;
121125
}
122126

127+
public void setDescription(String description) {
128+
this.description = description;
129+
}
130+
123131
public Date getCreated() {
124132
return created;
125133
}

server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
3737
import org.apache.cloudstack.api.command.admin.backup.ListBackupProviderOfferingsCmd;
3838
import org.apache.cloudstack.api.command.admin.backup.ListBackupProvidersCmd;
39+
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
3940
import org.apache.cloudstack.api.command.user.backup.AssignVirtualMachineToBackupOfferingCmd;
4041
import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd;
4142
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
@@ -62,6 +63,7 @@
6263
import org.apache.cloudstack.poll.BackgroundPollTask;
6364
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
6465
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
66+
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
6567
import org.apache.commons.collections.CollectionUtils;
6668
import org.apache.log4j.Logger;
6769

@@ -767,6 +769,7 @@ public List<Class<?>> getCommands() {
767769
cmdList.add(ImportBackupOfferingCmd.class);
768770
cmdList.add(ListBackupOfferingsCmd.class);
769771
cmdList.add(DeleteBackupOfferingCmd.class);
772+
cmdList.add(UpdateBackupOfferingCmd.class);
770773
// Assignment
771774
cmdList.add(AssignVirtualMachineToBackupOfferingCmd.class);
772775
cmdList.add(RemoveVirtualMachineFromBackupOfferingCmd.class);
@@ -1050,4 +1053,42 @@ public Long getDelay() {
10501053
return BackupSyncPollingInterval.value() * 1000L;
10511054
}
10521055
}
1056+
1057+
@Override
1058+
@ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_EDIT, eventDescription = "updating backup offering")
1059+
public BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd) {
1060+
Long id = updateBackupOfferingCmd.getId();
1061+
String name = updateBackupOfferingCmd.getName();
1062+
String description = updateBackupOfferingCmd.getDescription();
1063+
1064+
BackupOfferingVO backupOfferingVO = backupOfferingDao.findById(id);
1065+
if (backupOfferingVO == null) {
1066+
throw new InvalidParameterValueException(String.format("Unable to find Backup Offering with id: [%s].", id));
1067+
}
1068+
1069+
LOG.debug(String.format("Trying to update Backup Offering [id: %s, name: %s, description: %s] to [name: %s, description: %s].",
1070+
backupOfferingVO.getUuid(), backupOfferingVO.getName(), backupOfferingVO.getDescription(), name, description));
1071+
1072+
BackupOfferingVO offering = backupOfferingDao.createForUpdate(id);
1073+
List<String> fields = new ArrayList<>();
1074+
if (name != null) {
1075+
offering.setName(name);
1076+
fields.add("name: " + name);
1077+
}
1078+
1079+
if (description != null) {
1080+
offering.setDescription(description);
1081+
fields.add("description: " + description);
1082+
}
1083+
1084+
if (!backupOfferingDao.update(id, offering)) {
1085+
LOG.warn(String.format("Couldn't update Backup offering [id: %s] with [%s].", id, String.join(", ", fields)));
1086+
}
1087+
1088+
BackupOfferingVO response = backupOfferingDao.findById(id);
1089+
CallContext.current().setEventDetails(String.format("Backup Offering updated [%s].",
1090+
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(response, "id", "name", "description", "externalId")));
1091+
return response;
1092+
}
1093+
10531094
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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.backup;
18+
import static org.junit.Assert.assertEquals;
19+
import static org.mockito.Mockito.when;
20+
21+
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
22+
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
import org.mockito.InjectMocks;
26+
import org.mockito.Mock;
27+
import org.mockito.Mockito;
28+
import org.mockito.MockitoAnnotations;
29+
import org.mockito.Spy;
30+
31+
import com.cloud.exception.InvalidParameterValueException;
32+
33+
public class BackupManagerTest {
34+
@Spy
35+
@InjectMocks
36+
BackupManagerImpl backupManager = new BackupManagerImpl();
37+
38+
@Mock
39+
BackupOfferingDao backupOfferingDao;
40+
41+
@Before
42+
public void setup() throws Exception {
43+
MockitoAnnotations.initMocks(this);
44+
when(backupOfferingDao.findById(null)).thenReturn(null);
45+
when(backupOfferingDao.findById(123l)).thenReturn(null);
46+
47+
BackupOfferingVO offering = Mockito.spy(BackupOfferingVO.class);
48+
when(offering.getId()).thenReturn(1234l);
49+
when(offering.getName()).thenCallRealMethod();
50+
when(offering.getDescription()).thenCallRealMethod();
51+
52+
BackupOfferingVO offeringUpdate = Mockito.spy(BackupOfferingVO.class);
53+
when(offeringUpdate.getId()).thenReturn(1234l);
54+
when(offeringUpdate.getName()).thenReturn("Old name");
55+
when(offeringUpdate.getDescription()).thenReturn("Old description");
56+
57+
when(backupOfferingDao.findById(1234l)).thenReturn(offering);
58+
when(backupOfferingDao.createForUpdate(1234l)).thenReturn(offeringUpdate);
59+
when(backupOfferingDao.update(1234l, offeringUpdate)).thenAnswer(answer -> {
60+
offering.setName("New name");
61+
offering.setDescription("New description");
62+
return true;
63+
});
64+
}
65+
66+
@Test
67+
public void testExceptionWhenUpdateWithNullId() {
68+
try {
69+
Long id = null;
70+
71+
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
72+
when(cmd.getId()).thenReturn(id);
73+
74+
backupManager.updateBackupOffering(cmd);
75+
} catch (InvalidParameterValueException e) {
76+
assertEquals("Unable to find Backup Offering with id: [null].", e.getMessage());
77+
}
78+
}
79+
80+
@Test
81+
public void testExceptionWhenUpdateWithNonExistentId() {
82+
try {
83+
Long id = 123l;
84+
85+
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
86+
when(cmd.getId()).thenReturn(id);
87+
88+
backupManager.updateBackupOffering(cmd);
89+
} catch (InvalidParameterValueException e) {
90+
assertEquals("Unable to find Backup Offering with id: [123].", e.getMessage());
91+
}
92+
}
93+
94+
@Test
95+
public void testExceptionWhenUpdateWithoutChanges() {
96+
try {
97+
Long id = 1234l;
98+
99+
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
100+
when(cmd.getId()).thenReturn(id);
101+
when(cmd.getName()).thenReturn(null);
102+
when(cmd.getDescription()).thenReturn(null);
103+
104+
backupManager.updateBackupOffering(cmd);
105+
} catch (InvalidParameterValueException e) {
106+
assertEquals("Can't update Backup Offering [id: 1234] because there is no change in name or description.", e.getMessage());
107+
}
108+
}
109+
110+
@Test
111+
public void testUpdateBackupOfferingSuccess() {
112+
Long id = 1234l;
113+
114+
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
115+
when(cmd.getId()).thenReturn(id);
116+
when(cmd.getName()).thenReturn("New name");
117+
when(cmd.getDescription()).thenReturn("New description");
118+
119+
BackupOffering updated = backupManager.updateBackupOffering(cmd);
120+
assertEquals("New name", updated.getName());
121+
assertEquals("New description", updated.getDescription());
122+
}
123+
}

0 commit comments

Comments
 (0)