EC2 API fidelity: Implement support for image attribute - launchPermission

Now SOAP EC2 API supports launchPermission attribute for following commands:
- ec2-describe-image-attribute
- ec2-modify-image-attribute
- ec2-reset-image-attribute

Note:
- REST API is not changed.
- ec2 tools have some validation for userId to be AWS account id (8 digits). SO its is not possible to test adding or removing individual userIds in  ec2-modify-image-attribute command.
This commit is contained in:
prachi 2012-05-07 11:36:51 -07:00
parent 43f0d0df35
commit 31bf4eb426
8 changed files with 449 additions and 49 deletions

View File

@ -37,8 +37,9 @@ public class EC2MainServlet extends HttpServlet{
if(value != null){
isEC2APIEnabled = Boolean.valueOf(value);
}
PersistContext.commitTransaction(true);
}finally {
PersistContext.commitTransaction(true);
PersistContext.closeSession(true);
}

View File

@ -36,6 +36,8 @@ import com.cloud.bridge.service.core.ec2.EC2DescribeAddresses;
import com.cloud.bridge.service.core.ec2.EC2DescribeAddressesResponse;
import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZones;
import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZonesResponse;
import com.cloud.bridge.service.core.ec2.EC2DescribeImageAttribute;
import com.cloud.bridge.service.core.ec2.EC2DescribeImages;
import com.cloud.bridge.service.core.ec2.EC2DescribeImagesResponse;
import com.cloud.bridge.service.core.ec2.EC2DescribeInstances;
@ -53,11 +55,14 @@ import com.cloud.bridge.service.core.ec2.EC2Engine;
import com.cloud.bridge.service.core.ec2.EC2Filter;
import com.cloud.bridge.service.core.ec2.EC2GroupFilterSet;
import com.cloud.bridge.service.core.ec2.EC2Image;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute;
import com.cloud.bridge.service.core.ec2.EC2ImportKeyPair;
import com.cloud.bridge.service.core.ec2.EC2Instance;
import com.cloud.bridge.service.core.ec2.EC2InstanceFilterSet;
import com.cloud.bridge.service.core.ec2.EC2IpPermission;
import com.cloud.bridge.service.core.ec2.EC2KeyPairFilterSet;
import com.cloud.bridge.service.core.ec2.EC2ModifyImageAttribute;
import com.cloud.bridge.service.core.ec2.EC2PasswordData;
import com.cloud.bridge.service.core.ec2.EC2RebootInstances;
import com.cloud.bridge.service.core.ec2.EC2RegisterImage;
@ -226,18 +231,25 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface {
/**
* This only supports a query about description.
*/
public DescribeImageAttributeResponse describeImageAttribute(DescribeImageAttribute describeImageAttribute) {
EC2DescribeImages request = new EC2DescribeImages();
DescribeImageAttributeType diat = describeImageAttribute.getDescribeImageAttribute();
DescribeImageAttributesGroup diag = diat.getDescribeImageAttributesGroup();
EmptyElementType description = diag.getDescription();
public DescribeImageAttributeResponse describeImageAttribute(DescribeImageAttribute describeImageAttribute) {
EC2DescribeImageAttribute request = new EC2DescribeImageAttribute();
DescribeImageAttributeType diat = describeImageAttribute.getDescribeImageAttribute();
DescribeImageAttributesGroup diag = diat.getDescribeImageAttributesGroup();
EmptyElementType description = diag.getDescription();
EmptyElementType launchPermission = diag.getLaunchPermission();
if ( null != description ) {
request.setImageId(diat.getImageId());
request.setAttribute(ImageAttribute.description);
return toDescribeImageAttributeResponse( engine.describeImageAttribute( request ));
}else if(launchPermission != null){
request.setImageId(diat.getImageId());
request.setAttribute(ImageAttribute.launchPermission);
return toDescribeImageAttributeResponse( engine.describeImageAttribute( request ));
}
else throw new EC2ServiceException( "Unsupported - only description or launchPermission supported", 501 );
}
if ( null != description ) {
request.addImageSet(diat.getImageId());
return toDescribeImageAttributeResponse( engine.describeImages( request ));
}
else throw new EC2ServiceException( "Unsupported - only description supported", 501 );
}
public DescribeImagesResponse describeImages(DescribeImages describeImages) {
EC2DescribeImages request = new EC2DescribeImages();
@ -446,26 +458,50 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface {
}
public ModifyImageAttributeResponse modifyImageAttribute(ModifyImageAttribute modifyImageAttribute) {
// TODO: This is broken
EC2Image request = new EC2Image();
EC2ModifyImageAttribute request = new EC2ModifyImageAttribute();
ModifyImageAttributeType miat = modifyImageAttribute.getModifyImageAttribute();
ModifyImageAttributeTypeChoice_type0 item = miat.getModifyImageAttributeTypeChoice_type0();
AttributeValueType description = item.getDescription();
/*
LaunchPermissionOperationType launchPermOp = item.getLaunchPermission();
ProductCodeListType prodCodeList =item.getProductCodes();
*/
if (null != description) {
request.setId( miat.getImageId());
request.setImageId(miat.getImageId());
request.setAttribute(ImageAttribute.description);
request.setDescription(description.getValue());
return toModifyImageAttributeResponse( engine.modifyImageAttribute( request ));
}else if(launchPermOp != null){
request.setImageId(miat.getImageId());
request.setAttribute(ImageAttribute.launchPermission);
if(launchPermOp.getAdd() != null){
request.setLaunchPermOperation(EC2ModifyImageAttribute.Operation.add);
setAccountOrGroupList(launchPermOp.getAdd().getItem(), request);
}else if(launchPermOp.getRemove() != null){
request.setLaunchPermOperation(EC2ModifyImageAttribute.Operation.remove);
setAccountOrGroupList(launchPermOp.getRemove().getItem(), request);
}
return toModifyImageAttributeResponse( engine.modifyImageAttribute( request ));
}
throw new EC2ServiceException( "Unsupported - can only modify image description", 501 );
throw new EC2ServiceException( "Unsupported - can only modify image description or launchPermission", 501 );
}
private void setAccountOrGroupList(LaunchPermissionItemType[] items, EC2ModifyImageAttribute request){
List<String> launchPermissionAccountsOrGroupList = new ArrayList<String>();
for (LaunchPermissionItemType lpItem : items) {
if(lpItem.getGroup() != null){
launchPermissionAccountsOrGroupList.add(lpItem.getGroup());
}else if(lpItem.getUserId() != null){
launchPermissionAccountsOrGroupList.add(lpItem.getUserId());
}
}
request.setLaunchPermissionAccountsOrGroupList(launchPermissionAccountsOrGroupList);
}
/**
* Did not find a matching service offering so for now we just return disabled
* for each instance request. We could verify that all of the specified instances
@ -541,12 +577,16 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface {
*/
public ResetImageAttributeResponse resetImageAttribute(ResetImageAttribute resetImageAttribute) {
EC2Image request = new EC2Image();
EC2ModifyImageAttribute request = new EC2ModifyImageAttribute();
ResetImageAttributeType riat = resetImageAttribute.getResetImageAttribute();
request.setId( riat.getImageId());
request.setDescription( "" );
return toResetImageAttributeResponse( engine.modifyImageAttribute( request ));
EmptyElementType elementType = riat.getResetImageAttributesGroup().getLaunchPermission();
if(elementType != null){
request.setImageId( riat.getImageId());
request.setAttribute(ImageAttribute.launchPermission);
request.setLaunchPermOperation(EC2ModifyImageAttribute.Operation.reset);
return toResetImageAttributeResponse( engine.modifyImageAttribute( request ));
}
throw new EC2ServiceException( "Unsupported - can only reset image launchPermission", 501 );
}
/**
@ -678,6 +718,45 @@ public class EC2SoapServiceImpl implements AmazonEC2SkeletonInterface {
return response;
}
public static DescribeImageAttributeResponse toDescribeImageAttributeResponse(EC2ImageAttributes engineResponse) {
DescribeImageAttributeResponse response = new DescribeImageAttributeResponse();
DescribeImageAttributeResponseType param1 = new DescribeImageAttributeResponseType();
if (engineResponse != null ) {
DescribeImageAttributeResponseTypeChoice_type0 param2 = new DescribeImageAttributeResponseTypeChoice_type0();
if(engineResponse.getIsPublic()){
LaunchPermissionListType param3 = new LaunchPermissionListType();
LaunchPermissionItemType param4 = new LaunchPermissionItemType();
param4.setGroup("all");
param3.addItem(param4);
param2.setLaunchPermission(param3);
}else if(engineResponse.getAccountNamesWithLaunchPermission() != null){
LaunchPermissionListType param3 = new LaunchPermissionListType();
for(String accountName : engineResponse.getAccountNamesWithLaunchPermission()){
LaunchPermissionItemType param4 = new LaunchPermissionItemType();
param4.setUserId(accountName);
param3.addItem(param4);
}
param2.setLaunchPermission(param3);
}else if(engineResponse.getDescription() != null){
NullableAttributeValueType param3 = new NullableAttributeValueType();
param3.setValue( engineResponse.getDescription());
param2.setDescription( param3 );
}
param1.setDescribeImageAttributeResponseTypeChoice_type0( param2 );
param1.setImageId(engineResponse.getImageId());
}
param1.setRequestId( UUID.randomUUID().toString());
response.setDescribeImageAttributeResponse( param1 );
return response;
}
public static ModifyImageAttributeResponse toModifyImageAttributeResponse( boolean engineResponse ) {
ModifyImageAttributeResponse response = new ModifyImageAttributeResponse();
ModifyImageAttributeResponseType param1 = new ModifyImageAttributeResponseType();

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 Citrix Systems, Inc. All rights reserved.
*
* Licensed 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 com.cloud.bridge.service.core.ec2;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute;
public class EC2DescribeImageAttribute {
private String imageId = null; //AMI id
private ImageAttribute attribute = null;
public EC2DescribeImageAttribute() {
}
public void setAttribute(ImageAttribute attribute) {
this.attribute = attribute;
}
public ImageAttribute getAttribute() {
return attribute;
}
public void setImageId(String imageId) {
this.imageId = imageId;
}
public String getImageId() {
return imageId;
}
}

View File

@ -31,11 +31,14 @@ import java.util.UUID;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger;
import org.hibernate.ejb.criteria.expression.UnaryArithmeticOperation.Operation;
import org.xml.sax.SAXException;
import com.cloud.bridge.persist.dao.CloudStackSvcOfferingDao;
import com.cloud.bridge.persist.dao.OfferingDao;
import com.cloud.bridge.service.UserContext;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute;
import com.cloud.bridge.service.exception.EC2ServiceException;
import com.cloud.bridge.service.exception.EC2ServiceException.ClientError;
import com.cloud.bridge.service.exception.EC2ServiceException.ServerError;
@ -59,6 +62,7 @@ import com.cloud.stack.models.CloudStackSecurityGroupIngress;
import com.cloud.stack.models.CloudStackServiceOffering;
import com.cloud.stack.models.CloudStackSnapshot;
import com.cloud.stack.models.CloudStackTemplate;
import com.cloud.stack.models.CloudStackTemplatePermission;
import com.cloud.stack.models.CloudStackUser;
import com.cloud.stack.models.CloudStackUserVm;
import com.cloud.stack.models.CloudStackVolume;
@ -518,6 +522,34 @@ public class EC2Engine {
throw new EC2ServiceException(ServerError.InternalError, e.getMessage() != null ? e.getMessage() : "An unexpected error occurred.");
}
}
/** REST API calls this method.
* Modify an existing template
*
* @param request
* @return
*/
public boolean modifyImageAttribute( EC2Image request )
{
// TODO: This is incomplete
EC2DescribeImagesResponse images = new EC2DescribeImagesResponse();
try {
images = listTemplates( request.getId(), images );
EC2Image[] imageSet = images.getImageSet();
CloudStackTemplate resp = getApi().updateTemplate(request.getId(), null, request.getDescription(), null, imageSet[0].getName(), null, null);
if (resp != null) {
return true;
}
return false;
} catch( Exception e ) {
logger.error( "EC2 ModifyImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
}
/**
* Modify an existing template
@ -525,25 +557,87 @@ public class EC2Engine {
* @param request
* @return
*/
public boolean modifyImageAttribute( EC2Image request )
public boolean modifyImageAttribute( EC2ModifyImageAttribute request )
{
// TODO: This is incomplete
EC2DescribeImagesResponse images = new EC2DescribeImagesResponse();
try {
if(request.getAttribute().equals(ImageAttribute.launchPermission)){
String accounts = "";
Boolean isPublic = null;
EC2ModifyImageAttribute.Operation operation = request.getLaunchPermOperation();
List<String> accountOrGroupList = request.getLaunchPermissionAccountsList();
if(accountOrGroupList != null && !accountOrGroupList.isEmpty()){
boolean first = true;
for(String accountOrGroup : accountOrGroupList){
if("all".equalsIgnoreCase(accountOrGroup)){
if(operation.equals(EC2ModifyImageAttribute.Operation.add)){
isPublic = true;
}else{
isPublic = false;
}
}else{
if(!first){
accounts = accounts + ",";
}
accounts = accounts + accountOrGroup;
first = false;
}
}
}
CloudStackInfoResponse resp = getApi().updateTemplatePermissions(request.getImageId(), accounts, null, null, isPublic, operation.toString());
return resp.getSuccess();
}else if(request.getAttribute().equals(ImageAttribute.description)){
CloudStackTemplate resp = getApi().updateTemplate(request.getImageId(), null, request.getDescription(), null, null, null, null);
if (resp != null) {
return true;
}
return false;
}
try {
images = listTemplates( request.getId(), images );
EC2Image[] imageSet = images.getImageSet();
CloudStackTemplate resp = getApi().updateTemplate(request.getId(), null, request.getDescription(), null, imageSet[0].getName(), null, null);
if (resp != null) {
return true;
}
return false;
} catch( Exception e ) {
logger.error( "EC2 ModifyImage - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
} catch (Exception e) {
logger.error( "EC2 modifyImageAttribute - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return false;
}
public EC2ImageAttributes describeImageAttribute(EC2DescribeImageAttribute request) {
EC2ImageAttributes imageAtts = new EC2ImageAttributes();
try {
imageAtts.setImageId(request.getImageId());
if(request.getAttribute().equals(ImageAttribute.launchPermission)){
CloudStackTemplatePermission tempPerm = getApi().listTemplatePermissions(request.getImageId(), null, null);
if(tempPerm != null){
imageAtts.setDomainId(tempPerm.getDomainId());
List<String> accntList = tempPerm.getAccounts();
imageAtts.setAccountNamesWithLaunchPermission(accntList);
imageAtts.setIsPublic(tempPerm.getIsPublic());
}
}else if(request.getAttribute().equals(ImageAttribute.description)){
EC2DescribeImagesResponse descriptionResp = new EC2DescribeImagesResponse();
listTemplates(request.getImageId(), descriptionResp);
if(descriptionResp.getImageSet() != null){
EC2Image[] images = descriptionResp.getImageSet();
imageAtts.setDescription(images[0].getDescription());
}
}
} catch (Exception e) {
logger.error( "EC2 describeImageAttribute - ", e);
throw new EC2ServiceException(ServerError.InternalError, e.getMessage());
}
return imageAtts;
}
/**
* If given a specific list of snapshots of interest, then only values from those snapshots are returned.

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2011 Citrix Systems, Inc. All rights reserved.
*
* Licensed 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 com.cloud.bridge.service.core.ec2;
import java.util.List;
public class EC2ImageAttributes {
private String imageId;
private String description;
private boolean isPublic;
private List<String> accountNamesWithLaunchPermission;
private String domainId;
public enum ImageAttribute{
description,
launchPermission,
kernel,
ramdisk,
productCodes,
blockDeviceMapping
};
public EC2ImageAttributes() {
imageId = null;
description = null;
isPublic = false;
accountNamesWithLaunchPermission= null;
domainId = null;
}
public void setImageId( String imageId ) {
this.imageId = imageId;
}
public String getImageId() {
return this.imageId;
}
public void setDescription( String description ) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setIsPublic( boolean isPublic ) {
this.isPublic = isPublic;
}
public boolean getIsPublic() {
return this.isPublic;
}
public String getDomainId() {
return domainId;
}
public void setDomainId(String domainId) {
this.domainId = domainId;
}
public void setAccountNamesWithLaunchPermission(List<String> accountNamesWithLaunchPermission) {
this.accountNamesWithLaunchPermission = accountNamesWithLaunchPermission;
}
public List<String> getAccountNamesWithLaunchPermission() {
return accountNamesWithLaunchPermission;
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2011 Citrix Systems, Inc. All rights reserved.
*
* Licensed 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 com.cloud.bridge.service.core.ec2;
import java.util.List;
import com.cloud.bridge.service.core.ec2.EC2ImageAttributes.ImageAttribute;
public class EC2ModifyImageAttribute {
private String imageId = null; //AMI id
private ImageAttribute attribute = null;
private String description;
private Boolean isPublic = null;
private Operation launchPermOperation = null;
public enum Operation{
add,
remove,
reset;
}
private List<String> launchPermissionAccountsOrGroupList;
public EC2ModifyImageAttribute() {
}
public void setAttribute(ImageAttribute attribute) {
this.attribute = attribute;
}
public ImageAttribute getAttribute() {
return attribute;
}
public void setImageId(String imageId) {
this.imageId = imageId;
}
public String getImageId() {
return imageId;
}
public void setDescription( String description ) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setLaunchPermissionAccountsOrGroupList(List<String> launchPermissionAccountsOrGroupList) {
this.launchPermissionAccountsOrGroupList = launchPermissionAccountsOrGroupList;
}
public List<String> getLaunchPermissionAccountsList() {
return launchPermissionAccountsOrGroupList;
}
public void setLaunchPermOperation( Operation launchPermOperation ) {
this.launchPermOperation = launchPermOperation;
}
public Operation getLaunchPermOperation() {
return this.launchPermOperation;
}
public void setIsPublic(Boolean isPublic) {
this.isPublic = isPublic;
}
public Boolean getIsPublic() {
return isPublic;
}
}

View File

@ -566,15 +566,15 @@ public class CloudStackApi {
* @return
* @throws Exception
*/
public List<CloudStackTemplatePermission> listTemplatePermissions(String id, String account, String domainId) throws Exception {
public CloudStackTemplatePermission listTemplatePermissions(String id, String account, String domainId) throws Exception {
CloudStackCommand cmd = new CloudStackCommand(ApiConstants.LIST_TEMPLATE_PERMISSIONS);
if (cmd != null) {
cmd.setParam(ApiConstants.ID, id);
if (account != null) cmd.setParam(ApiConstants.ACCOUNT, account);
if (domainId != null) cmd.setParam(ApiConstants.DOMAIN_ID, domainId);
}
return _client.listCall(cmd, apiKey, secretKey, ApiConstants.LIST_TEMPLATE_PERMISSIONS_RESPONSE, ApiConstants.TEMPLATE_PERMISSION,
new TypeToken<List<CloudStackTemplatePermission>>() {}.getType());
return _client.call(cmd, apiKey, secretKey, false, ApiConstants.LIST_TEMPLATE_PERMISSIONS_RESPONSE, ApiConstants.TEMPLATE_PERMISSION,
CloudStackTemplatePermission.class);
}
/**

View File

@ -26,13 +26,13 @@ import com.google.gson.annotations.SerializedName;
*/
public class CloudStackTemplatePermission {
@SerializedName(ApiConstants.ID)
private Long id;
private String id;
@SerializedName(ApiConstants.IS_PUBLIC)
private Boolean isPublic;
@SerializedName(ApiConstants.DOMAIN_ID)
private Long domainId;
private String domainId;
@SerializedName(ApiConstants.ACCOUNT)
private List<CloudStackAccount> accounts;
private List<String> accounts;
/**
@ -45,7 +45,7 @@ public class CloudStackTemplatePermission {
/**
* @return the id
*/
public Long getId() {
public String getId() {
return id;
}
@ -61,7 +61,7 @@ public class CloudStackTemplatePermission {
/**
* @return the domainId
*/
public Long getDomainId() {
public String getDomainId() {
return domainId;
}
@ -69,7 +69,7 @@ public class CloudStackTemplatePermission {
/**
* @return the accounts
*/
public List<CloudStackAccount> getAccounts() {
public List<String> getAccounts() {
return accounts;
}