Create UpdateBackupOffering API (#5511)

* Create UpdateBackupOffering API

* Address reviews

* Address reviews

* Address reviews

Co-authored-by: SadiJr <sadi@scclouds.com.br>
This commit is contained in:
SadiJr 2021-10-01 08:30:25 -03:00 committed by GitHub
parent df0c0045b5
commit 965a47fdfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 283 additions and 0 deletions

View File

@ -521,6 +521,7 @@ public class EventTypes {
public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE";
public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE";
public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC";
public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT";
// external network device events
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";

View File

@ -0,0 +1,107 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.admin.backup;
import javax.inject.Inject;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.BackupOfferingResponse;
import org.apache.cloudstack.backup.BackupManager;
import org.apache.cloudstack.backup.BackupOffering;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.utils.exception.CloudRuntimeException;
@APICommand(name = "updateBackupOffering", description = "Updates a backup offering.", responseObject = BackupOfferingResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0")
public class UpdateBackupOfferingCmd extends BaseCmd {
private static final Logger LOGGER = Logger.getLogger(UpdateBackupOfferingCmd.class.getName());
private static final String APINAME = "updateBackupOffering";
@Inject
private BackupManager backupManager;
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupOfferingResponse.class, required = true, description = "The ID of the Backup Offering to be updated")
private Long id;
@Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, description = "The description of the Backup Offering to be updated")
private String description;
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Backup Offering to be updated")
private String name;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public void execute() {
try {
if (StringUtils.isAllEmpty(name, description)) {
throw new InvalidParameterValueException(String.format("Can't update Backup Offering [id: %s] because there is no change in name or description.", id));
}
BackupOffering result = backupManager.updateBackupOffering(this);
if (result == null) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to update backup offering [id: %s, name: %s, description: %s].", id, name, description));
}
BackupOfferingResponse response = _responseGenerator.createBackupOfferingResponse(result);
response.setResponseName(getCommandName());
this.setResponseObject(response);
} catch (CloudRuntimeException e) {
ApiErrorCode paramError = e instanceof InvalidParameterValueException ? ApiErrorCode.PARAM_ERROR : ApiErrorCode.INTERNAL_ERROR;
LOGGER.error(String.format("Failed to update Backup Offering [id: %s] due to: [%s].", id, e.getMessage()), e);
throw new ServerApiException(paramError, e.getMessage());
}
}
@Override
public String getCommandName() {
return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX;
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
}

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.backup;
import java.util.List;
import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd;
import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd;
@ -137,4 +138,6 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer
* @return returns operation success
*/
boolean deleteBackup(final Long backupId);
BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd);
}

View File

@ -93,6 +93,10 @@ public class BackupOfferingVO implements BackupOffering {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getExternalId() {
return externalId;
}
@ -120,6 +124,10 @@ public class BackupOfferingVO implements BackupOffering {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getCreated() {
return created;
}

View File

@ -36,6 +36,7 @@ import org.apache.cloudstack.api.command.admin.backup.DeleteBackupOfferingCmd;
import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd;
import org.apache.cloudstack.api.command.admin.backup.ListBackupProviderOfferingsCmd;
import org.apache.cloudstack.api.command.admin.backup.ListBackupProvidersCmd;
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
import org.apache.cloudstack.api.command.user.backup.AssignVirtualMachineToBackupOfferingCmd;
import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd;
import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd;
@ -62,6 +63,7 @@ import org.apache.cloudstack.poll.BackgroundPollManager;
import org.apache.cloudstack.poll.BackgroundPollTask;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
@ -767,6 +769,7 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
cmdList.add(ImportBackupOfferingCmd.class);
cmdList.add(ListBackupOfferingsCmd.class);
cmdList.add(DeleteBackupOfferingCmd.class);
cmdList.add(UpdateBackupOfferingCmd.class);
// Assignment
cmdList.add(AssignVirtualMachineToBackupOfferingCmd.class);
cmdList.add(RemoveVirtualMachineFromBackupOfferingCmd.class);
@ -1050,4 +1053,42 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager {
return BackupSyncPollingInterval.value() * 1000L;
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_EDIT, eventDescription = "updating backup offering")
public BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd) {
Long id = updateBackupOfferingCmd.getId();
String name = updateBackupOfferingCmd.getName();
String description = updateBackupOfferingCmd.getDescription();
BackupOfferingVO backupOfferingVO = backupOfferingDao.findById(id);
if (backupOfferingVO == null) {
throw new InvalidParameterValueException(String.format("Unable to find Backup Offering with id: [%s].", id));
}
LOG.debug(String.format("Trying to update Backup Offering [id: %s, name: %s, description: %s] to [name: %s, description: %s].",
backupOfferingVO.getUuid(), backupOfferingVO.getName(), backupOfferingVO.getDescription(), name, description));
BackupOfferingVO offering = backupOfferingDao.createForUpdate(id);
List<String> fields = new ArrayList<>();
if (name != null) {
offering.setName(name);
fields.add("name: " + name);
}
if (description != null) {
offering.setDescription(description);
fields.add("description: " + description);
}
if (!backupOfferingDao.update(id, offering)) {
LOG.warn(String.format("Couldn't update Backup offering [id: %s] with [%s].", id, String.join(", ", fields)));
}
BackupOfferingVO response = backupOfferingDao.findById(id);
CallContext.current().setEventDetails(String.format("Backup Offering updated [%s].",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(response, "id", "name", "description", "externalId")));
return response;
}
}

View File

@ -0,0 +1,123 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.backup;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd;
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import com.cloud.exception.InvalidParameterValueException;
public class BackupManagerTest {
@Spy
@InjectMocks
BackupManagerImpl backupManager = new BackupManagerImpl();
@Mock
BackupOfferingDao backupOfferingDao;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
when(backupOfferingDao.findById(null)).thenReturn(null);
when(backupOfferingDao.findById(123l)).thenReturn(null);
BackupOfferingVO offering = Mockito.spy(BackupOfferingVO.class);
when(offering.getId()).thenReturn(1234l);
when(offering.getName()).thenCallRealMethod();
when(offering.getDescription()).thenCallRealMethod();
BackupOfferingVO offeringUpdate = Mockito.spy(BackupOfferingVO.class);
when(offeringUpdate.getId()).thenReturn(1234l);
when(offeringUpdate.getName()).thenReturn("Old name");
when(offeringUpdate.getDescription()).thenReturn("Old description");
when(backupOfferingDao.findById(1234l)).thenReturn(offering);
when(backupOfferingDao.createForUpdate(1234l)).thenReturn(offeringUpdate);
when(backupOfferingDao.update(1234l, offeringUpdate)).thenAnswer(answer -> {
offering.setName("New name");
offering.setDescription("New description");
return true;
});
}
@Test
public void testExceptionWhenUpdateWithNullId() {
try {
Long id = null;
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
when(cmd.getId()).thenReturn(id);
backupManager.updateBackupOffering(cmd);
} catch (InvalidParameterValueException e) {
assertEquals("Unable to find Backup Offering with id: [null].", e.getMessage());
}
}
@Test
public void testExceptionWhenUpdateWithNonExistentId() {
try {
Long id = 123l;
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
when(cmd.getId()).thenReturn(id);
backupManager.updateBackupOffering(cmd);
} catch (InvalidParameterValueException e) {
assertEquals("Unable to find Backup Offering with id: [123].", e.getMessage());
}
}
@Test
public void testExceptionWhenUpdateWithoutChanges() {
try {
Long id = 1234l;
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
when(cmd.getId()).thenReturn(id);
when(cmd.getName()).thenReturn(null);
when(cmd.getDescription()).thenReturn(null);
backupManager.updateBackupOffering(cmd);
} catch (InvalidParameterValueException e) {
assertEquals("Can't update Backup Offering [id: 1234] because there is no change in name or description.", e.getMessage());
}
}
@Test
public void testUpdateBackupOfferingSuccess() {
Long id = 1234l;
UpdateBackupOfferingCmd cmd = Mockito.spy(UpdateBackupOfferingCmd.class);
when(cmd.getId()).thenReturn(id);
when(cmd.getName()).thenReturn("New name");
when(cmd.getDescription()).thenReturn("New description");
BackupOffering updated = backupManager.updateBackupOffering(cmd);
assertEquals("New name", updated.getName());
assertEquals("New description", updated.getDescription());
}
}