Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/src/main/java/com/cloud/server/ResourceTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public enum ResourceObjectType {
AutoScaleVmGroup(false, true),
LBStickinessPolicy(false, true),
LBHealthCheckPolicy(false, true),
SnapshotPolicy(false, true),
SnapshotPolicy(true, true),
GuestOs(false, true),
NetworkOffering(false, true),
VpcOffering(true, false);
Expand Down
3 changes: 2 additions & 1 deletion api/src/main/java/com/cloud/storage/VolumeApiService.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.cloud.storage;

import java.net.MalformedURLException;
import java.util.Map;

import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
Expand Down Expand Up @@ -93,7 +94,7 @@ public interface VolumeApiService {

Volume detachVolumeFromVM(DetachVolumeCmd cmd);

Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup)
Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map<String, String> tags)
throws ResourceAllocationException;

Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType) throws ResourceAllocationException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
// under the License.
package org.apache.cloudstack.api.command.user.snapshot;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandJobType;
import org.apache.cloudstack.api.ApiConstants;
Expand All @@ -28,6 +32,7 @@
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
import org.apache.cloudstack.api.response.SnapshotResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;

import com.cloud.event.EventTypes;
Expand Down Expand Up @@ -83,6 +88,9 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd {
@Parameter(name = ApiConstants.ASYNC_BACKUP, type = CommandType.BOOLEAN, required = false, description = "asynchronous backup if true")
private Boolean asyncBackup;

@Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)")
private Map tags;

private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject;

// ///////////////////////////////////////////////////
Expand Down Expand Up @@ -121,6 +129,18 @@ public Long getPolicyId() {
}
}

public Map<String, String> getTags() {
Map<String, String> tagsMap = new HashMap<>();
if (MapUtils.isNotEmpty(tags)) {
for (Map<String, String> services : (Collection<Map<String, String>>)tags.values()) {
String key = services.get("key");
String value = services.get("value");
tagsMap.put(key, value);
}
}
return tagsMap;
}

private Long getHostId() {
Volume volume = _entityMgr.findById(Volume.class, getVolumeId());
if (volume == null) {
Expand Down Expand Up @@ -196,7 +216,7 @@ public void execute() {
Snapshot snapshot;
try {
snapshot =
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup());
_volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags());

if (snapshot != null) {
SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
// under the License.
package org.apache.cloudstack.api.command.user.snapshot;

import org.apache.cloudstack.acl.RoleType;
import org.apache.log4j.Logger;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
Expand All @@ -27,6 +29,8 @@
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.SnapshotPolicyResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.commons.collections.MapUtils;
import org.apache.log4j.Logger;

import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
Expand Down Expand Up @@ -68,6 +72,9 @@ public class CreateSnapshotPolicyCmd extends BaseCmd {
@Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the policy to the end user or not", since = "4.4", authorized = {RoleType.Admin})
private Boolean display;

@Parameter(name = ApiConstants.TAGS, type = CommandType.MAP, description = "Map of tags (key/value pairs)")
private Map tags;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -133,6 +140,18 @@ public long getEntityOwnerId() {
return volume.getAccountId();
}

public Map<String, String> getTags() {
Map<String, String> tagsMap = new HashMap<>();
if (MapUtils.isNotEmpty(tags)) {
for (Map<String, String> services : (Collection<Map<String, String>>)tags.values()) {
String key = services.get("key");
String value = services.get("value");
tagsMap.put(key, value);
}
}
return tagsMap;
}

@Override
public void execute() {
SnapshotPolicy result = _snapshotService.createPolicy(this, _accountService.getAccount(getEntityOwnerId()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@
// under the License.
package org.apache.cloudstack.api.response;

import com.google.gson.annotations.SerializedName;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.BaseResponseWithTagInformation;
import org.apache.cloudstack.api.EntityReference;

import com.cloud.serializer.Param;
import com.cloud.storage.snapshot.SnapshotPolicy;
import com.google.gson.annotations.SerializedName;

@EntityReference(value = SnapshotPolicy.class)
public class SnapshotPolicyResponse extends BaseResponse {
public class SnapshotPolicyResponse extends BaseResponseWithTagInformation {
@SerializedName("id")
@Param(description = "the ID of the snapshot policy")
private String id;
Expand Down Expand Up @@ -56,6 +58,10 @@ public class SnapshotPolicyResponse extends BaseResponse {
@Param(description = "is this policy for display to the regular user", since = "4.4", authorized = {RoleType.Admin})
private Boolean forDisplay;

public SnapshotPolicyResponse() {
tags = new LinkedHashSet<ResourceTagResponse>();
}

public String getId() {
return id;
}
Expand Down Expand Up @@ -111,4 +117,8 @@ public Boolean isForDisplay() {
public void setForDisplay(Boolean forDisplay) {
this.forDisplay = forDisplay;
}

public void setTags(Set<ResourceTagResponse> tags) {
this.tags = tags;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,20 @@
// under the License.
package org.apache.cloudstack.api.response;

import com.cloud.serializer.Param;
import com.cloud.storage.Snapshot;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.BaseResponseWithTagInformation;
import org.apache.cloudstack.api.EntityReference;

import java.util.Date;
import java.util.List;
import com.cloud.serializer.Param;
import com.cloud.storage.Snapshot;
import com.google.gson.annotations.SerializedName;

@EntityReference(value = Snapshot.class)
public class SnapshotResponse extends BaseResponse implements ControlledEntityResponse {
public class SnapshotResponse extends BaseResponseWithTagInformation implements ControlledEntityResponse {
@SerializedName(ApiConstants.ID)
@Param(description = "ID of the snapshot")
private String id;
Expand Down Expand Up @@ -96,10 +98,6 @@ public class SnapshotResponse extends BaseResponse implements ControlledEntityRe
@Param(description = "id of the availability zone")
private String zoneId;

@SerializedName(ApiConstants.TAGS)
@Param(description = "the list of resource tags associated with snapshot", responseObject = ResourceTagResponse.class)
private List<ResourceTagResponse> tags;

@SerializedName(ApiConstants.REVERTABLE)
@Param(description = "indicates whether the underlying storage supports reverting the volume to this snapshot")
private boolean revertable;
Expand All @@ -116,6 +114,10 @@ public class SnapshotResponse extends BaseResponse implements ControlledEntityRe
@Param(description = "virtual size of backedup snapshot on image store")
private long virtualSize;

public SnapshotResponse() {
tags = new LinkedHashSet<ResourceTagResponse>();
}

@Override
public String getObjectId() {
return this.getId();
Expand Down Expand Up @@ -206,7 +208,7 @@ public void setZoneId(String zoneId) {
this.zoneId = zoneId;
}

public void setTags(List<ResourceTagResponse> tags) {
public void setTags(Set<ResourceTagResponse> tags) {
this.tags = tags;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.isNull;

import java.util.HashMap;
import java.util.Map;

import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotCmd;
Expand All @@ -32,6 +36,7 @@
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.test.util.ReflectionTestUtils;

import com.cloud.storage.Snapshot;
import com.cloud.storage.VolumeApiService;
Expand Down Expand Up @@ -87,9 +92,8 @@ public void testCreateSuccess() {
VolumeApiService volumeApiService = Mockito.mock(VolumeApiService.class);
Snapshot snapshot = Mockito.mock(Snapshot.class);
try {

Mockito.when(volumeApiService.takeSnapshot(anyLong(), anyLong(), anyLong(),
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean())).thenReturn(snapshot);
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean(), anyObject())).thenReturn(snapshot);

} catch (Exception e) {
Assert.fail("Received exception when success expected " + e.getMessage());
Expand Down Expand Up @@ -122,7 +126,7 @@ public void testCreateFailure() {

try {
Mockito.when(volumeApiService.takeSnapshot(anyLong(), anyLong(), anyLong(),
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean())).thenReturn(null);
any(Account.class), anyBoolean(), isNull(Snapshot.LocationType.class), anyBoolean(), anyObject())).thenReturn(null);
} catch (Exception e) {
Assert.fail("Received exception when success expected " + e.getMessage());
}
Expand All @@ -136,4 +140,24 @@ public void testCreateFailure() {
Assert.assertEquals("Failed to create snapshot due to an internal error creating snapshot for volume 123", exception.getDescription());
}
}

@Test
public void testParsingTags() {
final CreateSnapshotCmd createSnapshotCmd = new CreateSnapshotCmd();
final Map<String, String> tag1 = new HashMap<>();
tag1.put("key", "key1");
tag1.put("value", "value1");
final Map<String, String> tag2 = new HashMap<>();
tag2.put("key", "key2");
tag2.put("value", "value2");
final Map<String, String> expectedTags = new HashMap<>();
expectedTags.put("key1", "value1");
expectedTags.put("key2", "value2");

final Map<String, Map<String, String>> tagsParams = new HashMap<>();
tagsParams.put("0", tag1);
tagsParams.put("1", tag2);
ReflectionTestUtils.setField(createSnapshotCmd, "tags", tagsParams);
Assert.assertEquals(createSnapshotCmd.getTags(), expectedTags);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.user.snapshot;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

license needed, @shwstppr . Can you add it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaanHoogland added license


import java.util.HashMap;
import java.util.Map;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.test.util.ReflectionTestUtils;

public class CreateSnapshotPolicyCmdTest {
@Test
public void testParsingTags() {
final CreateSnapshotPolicyCmd createSnapshotPolicyCmd = new CreateSnapshotPolicyCmd();
final Map<String, String> tag1 = new HashMap<>();
tag1.put("key", "key1");
tag1.put("value", "value1");
final Map<String, String> tag2 = new HashMap<>();
tag2.put("key", "key2");
tag2.put("value", "value2");
final Map<String, String> expectedTags = new HashMap<>();
expectedTags.put("key1", "value1");
expectedTags.put("key2", "value2");

final Map<String, Map<String, String>> tagsParams = new HashMap<>();
tagsParams.put("0", tag1);
tagsParams.put("1", tag2);
ReflectionTestUtils.setField(createSnapshotPolicyCmd, "tags", tagsParams);
Assert.assertEquals(createSnapshotPolicyCmd.getTags(), expectedTags);
}
}
10 changes: 9 additions & 1 deletion server/src/main/java/com/cloud/api/ApiResponseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ public SnapshotResponse createSnapshotResponse(Snapshot snapshot) {
ResourceTagResponse tagResponse = createResourceTagResponse(tag, true);
CollectionUtils.addIgnoreNull(tagResponses, tagResponse);
}
snapshotResponse.setTags(tagResponses);
snapshotResponse.setTags(new HashSet<>(tagResponses));

snapshotResponse.setObjectName("snapshot");
return snapshotResponse;
Expand Down Expand Up @@ -654,6 +654,14 @@ public SnapshotPolicyResponse createSnapshotPolicyResponse(SnapshotPolicy policy
policyResponse.setForDisplay(policy.isDisplay());
policyResponse.setObjectName("snapshotpolicy");

List<? extends ResourceTag> tags = _resourceTagDao.listBy(policy.getId(), ResourceObjectType.SnapshotPolicy);
List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
for (ResourceTag tag : tags) {
ResourceTagResponse tagResponse = createResourceTagResponse(tag, false);
CollectionUtils.addIgnoreNull(tagResponses, tagResponse);
}
policyResponse.setTags(new HashSet<>(tagResponses));

return policyResponse;
}

Expand Down
Loading