/************************************************************************* * 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.AWSEC2SubnetNetworkAclAssociationResourceInfo; import com.eucalyptus.cloudformation.resources.standard.propertytypes.AWSEC2SubnetNetworkAclAssociationProperties; 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.updateinfo.UpdateType; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.Topology; import com.eucalyptus.compute.common.Compute; import com.eucalyptus.compute.common.DescribeNetworkAclsResponseType; import com.eucalyptus.compute.common.DescribeNetworkAclsType; import com.eucalyptus.compute.common.DescribeSubnetsResponseType; import com.eucalyptus.compute.common.DescribeSubnetsType; import com.eucalyptus.compute.common.Filter; import com.eucalyptus.compute.common.NetworkAclAssociationType; import com.eucalyptus.compute.common.NetworkAclType; import com.eucalyptus.compute.common.ReplaceNetworkAclAssociationResponseType; import com.eucalyptus.compute.common.ReplaceNetworkAclAssociationType; import com.eucalyptus.util.async.AsyncRequests; import com.fasterxml.jackson.databind.node.TextNode; import com.google.common.collect.Lists; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Objects; /** * Created by ethomas on 2/3/14. */ public class AWSEC2SubnetNetworkAclAssociationResourceAction extends StepBasedResourceAction { private AWSEC2SubnetNetworkAclAssociationProperties properties = new AWSEC2SubnetNetworkAclAssociationProperties(); private AWSEC2SubnetNetworkAclAssociationResourceInfo info = new AWSEC2SubnetNetworkAclAssociationResourceInfo(); public AWSEC2SubnetNetworkAclAssociationResourceAction() { super(fromEnum(CreateSteps.class), fromEnum(DeleteSteps.class), null, null); } @Override public UpdateType getUpdateType(ResourceAction resourceAction, boolean stackTagsChanged) { UpdateType updateType = info.supportsTags() && stackTagsChanged ? UpdateType.NO_INTERRUPTION : UpdateType.NONE; AWSEC2SubnetNetworkAclAssociationResourceAction otherAction = (AWSEC2SubnetNetworkAclAssociationResourceAction) resourceAction; if (!Objects.equals(properties.getNetworkAclId(), otherAction.properties.getNetworkAclId())) { updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT); } if (!Objects.equals(properties.getSubnetId(), otherAction.properties.getSubnetId())) { updateType = UpdateType.max(updateType, UpdateType.NEEDS_REPLACEMENT); } return updateType; } private enum CreateSteps implements Step { CREATE_ASSOCIATION { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSEC2SubnetNetworkAclAssociationResourceAction action = (AWSEC2SubnetNetworkAclAssociationResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Compute.class); // See if network acl is there action.checkNetworkAclExists(configuration); // See if subnet is there action.checkSubnetExists(configuration); // Find the current network acl so we can find the association id String associationId = action.getAssociationId(configuration); if (associationId == null) { throw new ValidationErrorException("Unable to find current network acl association id for subnet with id " + action.properties.getSubnetId()); } String newAssociationId = action.replaceAssociation(configuration, associationId, action.properties.getNetworkAclId()); action.info.setPhysicalResourceId(newAssociationId); action.info.setCreatedEnoughToDelete(true); action.info.setReferenceValueJson(JsonHelper.getStringFromJsonNode(new TextNode(action.info.getPhysicalResourceId()))); action.info.setAssociationId(JsonHelper.getStringFromJsonNode(new TextNode(newAssociationId))); return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } private enum DeleteSteps implements Step { DELETE_ASSOCIATION { @Override public ResourceAction perform(ResourceAction resourceAction) throws Exception { AWSEC2SubnetNetworkAclAssociationResourceAction action = (AWSEC2SubnetNetworkAclAssociationResourceAction) resourceAction; ServiceConfiguration configuration = Topology.lookup(Compute.class); if (!Boolean.TRUE.equals(action.info.getCreatedEnoughToDelete())) return action; // First see if association id is there... if (!action.associationIdExistsForDelete(configuration)) return action; if (!action.networkAclExistsForDelete(configuration)) return action; String vpcId = action.checkSubnetIdAndGetVpcIdForDelete(configuration); if (vpcId == null) return action; String defaultNetworkAclId = action.findDefaultNetworkAclId(configuration, vpcId); if (defaultNetworkAclId == null) { throw new ValidationErrorException("Unable to find the default network acl id for vpc " + vpcId); } action.replaceAssociation(configuration, action.info.getPhysicalResourceId(), defaultNetworkAclId); return action; } }; @Nullable @Override public Integer getTimeout() { return null; } } @Override public ResourceProperties getResourceProperties() { return properties; } @Override public void setResourceProperties(ResourceProperties resourceProperties) { properties = (AWSEC2SubnetNetworkAclAssociationProperties) resourceProperties; } @Override public ResourceInfo getResourceInfo() { return info; } @Override public void setResourceInfo(ResourceInfo resourceInfo) { info = (AWSEC2SubnetNetworkAclAssociationResourceInfo) resourceInfo; } private String replaceAssociation(ServiceConfiguration configuration, String associationId, String networkAclId) throws Exception { ReplaceNetworkAclAssociationType replaceNetworkAclAssociationType = MessageHelper.createMessage(ReplaceNetworkAclAssociationType.class, info.getEffectiveUserId()); replaceNetworkAclAssociationType.setAssociationId(associationId); replaceNetworkAclAssociationType.setNetworkAclId(networkAclId); ReplaceNetworkAclAssociationResponseType replaceNetworkAclAssociationResponseType = AsyncRequests.<ReplaceNetworkAclAssociationType, ReplaceNetworkAclAssociationResponseType> sendSync(configuration, replaceNetworkAclAssociationType); return replaceNetworkAclAssociationResponseType.getNewAssociationId(); } private void checkSubnetExists(ServiceConfiguration configuration) throws Exception { DescribeSubnetsType describeSubnetsType = MessageHelper.createMessage(DescribeSubnetsType.class, info.getEffectiveUserId()); describeSubnetsType.getFilterSet( ).add( Filter.filter( "subnet-id", properties.getSubnetId( ) ) ); DescribeSubnetsResponseType describeSubnetsResponseType = AsyncRequests.sendSync( configuration, describeSubnetsType ); if (describeSubnetsResponseType.getSubnetSet() == null || describeSubnetsResponseType.getSubnetSet().getItem() == null || describeSubnetsResponseType.getSubnetSet().getItem().isEmpty()) { throw new ValidationErrorException("No such subnet with id '" + properties.getSubnetId()); } } private void checkNetworkAclExists(ServiceConfiguration configuration) throws Exception { DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, info.getEffectiveUserId()); describeNetworkAclsType.getFilterSet( ).add( Filter.filter( "network-acl-id", 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 '" + properties.getNetworkAclId()); } } private String getAssociationId(ServiceConfiguration configuration) throws Exception { String associationId = null; DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, info.getEffectiveUserId()); ArrayList<Filter> filterSet = Lists.newArrayList();; Filter filter = new Filter(); filter.setName("association.subnet-id"); filter.setValueSet(Lists.<String>newArrayList(properties.getSubnetId())); filterSet.add(filter); describeNetworkAclsType.setFilterSet( filterSet ); DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.<DescribeNetworkAclsType, DescribeNetworkAclsResponseType> sendSync( configuration, describeNetworkAclsType ); if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) { throw new ValidationErrorException("Can not find existing network association for subnet " + properties.getSubnetId()); } if (describeNetworkAclsResponseType.getNetworkAclSet().getItem().size() > 1) { throw new ValidationErrorException("More than one existing network association exists for subnet " + properties.getSubnetId()); } if (describeNetworkAclsResponseType.getNetworkAclSet().getItem().get(0).getAssociationSet() != null && describeNetworkAclsResponseType.getNetworkAclSet().getItem().get(0).getAssociationSet().getItem() != null) { for (NetworkAclAssociationType networkAclAssociationType: describeNetworkAclsResponseType.getNetworkAclSet().getItem().get(0).getAssociationSet().getItem()) { if (properties.getSubnetId().equals(networkAclAssociationType.getSubnetId())) { associationId = networkAclAssociationType.getNetworkAclAssociationId(); break; } } } return associationId; } private String findDefaultNetworkAclId(ServiceConfiguration configuration, String vpcId) throws Exception{ DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, info.getEffectiveUserId()); ArrayList<Filter> filterSet = Lists.newArrayList();; Filter filter = new Filter(); filter.setName("vpc-id"); filter.setValueSet(Lists.<String>newArrayList(vpcId)); filterSet.add(filter); describeNetworkAclsType.setFilterSet(filterSet); DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.<DescribeNetworkAclsType, DescribeNetworkAclsResponseType> sendSync(configuration, describeNetworkAclsType); if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) { return null; } for (NetworkAclType networkAclType: describeNetworkAclsResponseType.getNetworkAclSet().getItem()) { if (vpcId.equals(networkAclType.getVpcId()) && networkAclType.get_default()) { return networkAclType.getNetworkAclId(); } } return null; } private String checkSubnetIdAndGetVpcIdForDelete(ServiceConfiguration configuration) throws Exception { DescribeSubnetsType describeSubnetsType = MessageHelper.createMessage(DescribeSubnetsType.class, info.getEffectiveUserId()); describeSubnetsType.getFilterSet( ).add( Filter.filter( "subnet-id", properties.getSubnetId( ) ) ); DescribeSubnetsResponseType describeSubnetsResponseType = AsyncRequests.sendSync( configuration, describeSubnetsType ); if (describeSubnetsResponseType.getSubnetSet() == null || describeSubnetsResponseType.getSubnetSet().getItem() == null || describeSubnetsResponseType.getSubnetSet().getItem().isEmpty()) { return null; } return describeSubnetsResponseType.getSubnetSet().getItem().get(0).getVpcId(); } private boolean networkAclExistsForDelete(ServiceConfiguration configuration) throws Exception { DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, info.getEffectiveUserId()); describeNetworkAclsType.getFilterSet( ).add( Filter.filter( "network-acl-id", properties.getNetworkAclId( ) ) ); DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.sendSync( configuration, describeNetworkAclsType ); if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) { return false; } return true; } private boolean associationIdExistsForDelete(ServiceConfiguration configuration) throws Exception { DescribeNetworkAclsType describeNetworkAclsType = MessageHelper.createMessage(DescribeNetworkAclsType.class, info.getEffectiveUserId()); ArrayList<Filter> filterSet = Lists.newArrayList();; Filter filter = new Filter(); filter.setName("association.association-id"); filter.setValueSet(Lists.<String>newArrayList(info.getPhysicalResourceId())); filterSet.add(filter); describeNetworkAclsType.setFilterSet(filterSet); DescribeNetworkAclsResponseType describeNetworkAclsResponseType = AsyncRequests.<DescribeNetworkAclsType, DescribeNetworkAclsResponseType> sendSync(configuration, describeNetworkAclsType); if (describeNetworkAclsResponseType.getNetworkAclSet() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem() == null || describeNetworkAclsResponseType.getNetworkAclSet().getItem().isEmpty()) { return false; } return true; } }