/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.cloudformation.resources.standard.actions;
import com.eucalyptus.cloudformation.ValidationErrorException;
import com.eucalyptus.cloudformation.resources.ResourceAction;
import com.eucalyptus.cloudformation.resources.ResourceInfo;
import com.eucalyptus.cloudformation.resources.ResourceProperties;
import com.eucalyptus.cloudformation.resources.standard.info.AWSEC2NetworkAclEntryResourceInfo;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSEC2NetworkAclEntryProperties;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2ICMP;
import com.eucalyptus.cloudformation.resources.standard.propertytypes.EC2PortRange;
import com.eucalyptus.cloudformation.template.JsonHelper;
import com.eucalyptus.cloudformation.util.MessageHelper;
import com.eucalyptus.cloudformation.workflow.steps.Step;
import com.eucalyptus.cloudformation.workflow.steps.StepBasedResourceAction;
import com.eucalyptus.cloudformation.workflow.steps.UpdateStep;
import com.eucalyptus.cloudformation.workflow.updateinfo.UpdateType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.compute.common.CreateNetworkAclEntryResponseType;
import com.eucalyptus.compute.common.CreateNetworkAclEntryType;
import com.eucalyptus.compute.common.DeleteNetworkAclEntryResponseType;
import com.eucalyptus.compute.common.DeleteNetworkAclEntryType;
import com.eucalyptus.compute.common.DescribeNetworkAclsResponseType;
import com.eucalyptus.compute.common.DescribeNetworkAclsType;
import com.eucalyptus.compute.common.Filter;
import com.eucalyptus.compute.common.IcmpTypeCodeType;
import com.eucalyptus.compute.common.NetworkAclEntryType;
import com.eucalyptus.compute.common.NetworkAclType;
import com.eucalyptus.compute.common.PortRangeType;
import com.eucalyptus.compute.common.ReplaceNetworkAclEntryResponseType;
import com.eucalyptus.compute.common.ReplaceNetworkAclEntryType;
import com.eucalyptus.util.async.AsyncRequests;
import com.fasterxml.jackson.databind.node.TextNode;
import javax.annotation.Nullable;
import java.util.Objects;
/**
* Created by ethomas on 2/3/14.
*/
public class AWSEC2NetworkAclEntryResourceAction extends StepBasedResourceAction {
private AWSEC2NetworkAclEntryProperties properties = new AWSEC2NetworkAclEntryProperties();
private AWSEC2NetworkAclEntryResourceInfo info = new AWSEC2NetworkAclEntryResourceInfo();
public AWSEC2NetworkAclEntryResourceAction() {
super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), fromUpdateEnum(UpdateNoInterruptionSteps.class), null);
}
@Override
public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) {
UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE;
AWSEC2NetworkAclEntryResourceAction otherAction = (AWSEC2NetworkAclEntryResourceAction) resourceAction;
if (!Objects.equals(properties.getCidrBlock(), otherAction.properties.getCidrBlock())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getEgress(), otherAction.properties.getEgress())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getIcmp(), otherAction.properties.getIcmp())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getNetworkAclId(), otherAction.properties.getNetworkAclId())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
if (!Objects.equals(properties.getPortRange(), otherAction.properties.getPortRange())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getProtocol(), otherAction.properties.getProtocol())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getRuleAction(), otherAction.properties.getRuleAction())) {
updateType = UpdateType.max(updateType, UpdateType.NO_INTERRUPTION);
}
if (!Objects.equals(properties.getRuleNumber(), otherAction.properties.getRuleNumber())) {
updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT);
}
return updateType;
}
private enum CreateSteps implements Step {
CREATE_NETWORK_ACL_ENTRY {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2NetworkAclEntryResourceAction action = (AWSEC2NetworkAclEntryResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
// See if network acl is there
if (action.properties.getNetworkAclId().isEmpty()) {
throw new ValidationErrorException("NetworkAclId is a required field");
}
DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, action.info.getEffectiveUserId());
describeNetworkAclsType.getFilterSet( ).add( Filter.filter( "network-acl-id", action.properties.getNetworkAclId() ) );
DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.sendSync(configuration, describeNetworkAclsType);
if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null ||
describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) {
throw new ValidationErrorException("No such network acl with id '" + action.properties.getNetworkAclId());
}
CreateNetworkAclEntryType createNetworkAclEntryType = MessageHelper.createMessage(CreateNetworkAclEntryType.class, action.info.getEffectiveUserId());
createNetworkAclEntryType.setCidrBlock(action.properties.getCidrBlock());
if (action.properties.getEgress() != null){
createNetworkAclEntryType.setEgress(action.properties.getEgress());
}
createNetworkAclEntryType.setIcmpTypeCode(action.convertIcmpTypeCode(action.properties.getIcmp()));
createNetworkAclEntryType.setNetworkAclId(action.properties.getNetworkAclId());
createNetworkAclEntryType.setPortRange(action.convertPortRange(action.properties.getPortRange()));
createNetworkAclEntryType.setProtocol(action.properties.getProtocol() == null ? null : String.valueOf(action.properties.getProtocol()));
createNetworkAclEntryType.setRuleAction(action.properties.getRuleAction());
createNetworkAclEntryType.setRuleNumber(action.properties.getRuleNumber());
CreateNetworkAclEntryResponseType CreateNetworkAclEntryResponseType = AsyncRequests.<CreateNetworkAclEntryType, CreateNetworkAclEntryResponseType>sendSync(configuration, createNetworkAclEntryType);
action.info.setPhysicalResourceId(action.getDefaultPhysicalResourceId());
action.info.setCreatedEnoughToDelete(true);
action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId())));
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum DeleteSteps implements Step {
DELETE_NETWORK_ACL_ENTRY {
@Override
public ResourceAction perform(ResourceAction resourceAction) throws Exception {
AWSEC2NetworkAclEntryResourceAction action = (AWSEC2NetworkAclEntryResourceAction) resourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action;
// See if network ACL is there
DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, action.info.getEffectiveUserId());
describeNetworkAclsType.getFilterSet( ).add( Filter.filter( "network-acl-id", action.properties.getNetworkAclId() ) );
DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.sendSync( configuration, describeNetworkAclsType);
if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null ||
describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) {
return action; // no network acl
}
// now see if the entry is in the rule
boolean foundEntry = false;
for (NetworkAclType networkAclType: describeNetworkAclsResponseType.getNetworkAclSet().getItem()) {
if (networkAclType.getEntrySet() == null || networkAclType.getEntrySet().getItem() == null) continue;
for (NetworkAclEntryType networkAclEntryType : networkAclType.getEntrySet().getItem()) {
if (action.properties.getRuleNumber().equals(networkAclEntryType.getRuleNumber()) && action.properties.getEgress().equals(networkAclEntryType.getEgress())) {
foundEntry = true;
break;
}
}
}
if (!foundEntry) return action; // no rule to remove
DeleteNetworkAclEntryType deleteNetworkAclEntryType = MessageHelper.createMessage(DeleteNetworkAclEntryType.class, action.info.getEffectiveUserId());
deleteNetworkAclEntryType.setNetworkAclId(action.properties.getNetworkAclId());
deleteNetworkAclEntryType.setEgress(action.properties.getEgress());
deleteNetworkAclEntryType.setRuleNumber(action.properties.getRuleNumber());
DeleteNetworkAclEntryResponseType deleteNetworkAclEntryResponseType = AsyncRequests.<DeleteNetworkAclEntryType, DeleteNetworkAclEntryResponseType> sendSync(configuration, deleteNetworkAclEntryType);
return action;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
private enum UpdateNoInterruptionSteps implements UpdateStep {
REPLACE_NETWORK_ACL_ENTRY {
@Override
public ResourceAction perform(ResourceAction oldResourceAction, ResourceAction newResourceAction) throws Exception {
AWSEC2NetworkAclEntryResourceAction oldAction = (AWSEC2NetworkAclEntryResourceAction) oldResourceAction;
AWSEC2NetworkAclEntryResourceAction newAction = (AWSEC2NetworkAclEntryResourceAction) newResourceAction;
ServiceConfiguration configuration = Topology.lookup(Compute.class);
ReplaceNetworkAclEntryType replaceNetworkAclEntryType = MessageHelper.createMessage(ReplaceNetworkAclEntryType.class, newAction.info.getEffectiveUserId());
replaceNetworkAclEntryType.setCidrBlock(newAction.properties.getCidrBlock());
if (newAction.properties.getEgress() != null){
replaceNetworkAclEntryType.setEgress(newAction.properties.getEgress());
}
replaceNetworkAclEntryType.setIcmpTypeCode(newAction.convertIcmpTypeCode(newAction.properties.getIcmp()));
replaceNetworkAclEntryType.setNetworkAclId(newAction.properties.getNetworkAclId());
replaceNetworkAclEntryType.setPortRange(newAction.convertPortRange(newAction.properties.getPortRange()));
replaceNetworkAclEntryType.setProtocol(newAction.properties.getProtocol() == null ? null : String.valueOf(newAction.properties.getProtocol()));
replaceNetworkAclEntryType.setRuleAction(newAction.properties.getRuleAction());
replaceNetworkAclEntryType.setRuleNumber(newAction.properties.getRuleNumber());
ReplaceNetworkAclEntryResponseType replaceNetworkAclEntryResponseType = AsyncRequests.<ReplaceNetworkAclEntryType, ReplaceNetworkAclEntryResponseType>sendSync(configuration, replaceNetworkAclEntryType);
return newAction;
}
};
@Nullable
@Override
public Integer getTimeout() {
return null;
}
}
@Override
public ResourceProperties getResourceProperties() {
return properties;
}
@Override
public void setResourceProperties(ResourceProperties resourceProperties) {
properties = (AWSEC2NetworkAclEntryProperties) resourceProperties;
}
@Override
public ResourceInfo getResourceInfo() {
return info;
}
@Override
public void setResourceInfo(ResourceInfo resourceInfo) {
info = (AWSEC2NetworkAclEntryResourceInfo) resourceInfo;
}
private PortRangeType convertPortRange(EC2PortRange portRange) {
if (portRange == null) return null;
PortRangeType portRangeType = new PortRangeType();
portRangeType.setFrom(portRange.getFrom());
portRangeType.setTo(portRange.getTo());
return portRangeType;
}
private IcmpTypeCodeType convertIcmpTypeCode(EC2ICMP icmp) {
if (icmp == null) return null;
IcmpTypeCodeType icmpTypeCodeType = new IcmpTypeCodeType();
icmpTypeCodeType.setCode(icmp.getCode());
icmpTypeCodeType.setType(icmp.getType());
return icmpTypeCodeType;
}
}