| // 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 com.cloud.network.vpc; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.inject.Inject; |
| |
| import com.cloud.configuration.ConfigurationManager; |
| import com.cloud.event.ActionEvent; |
| import com.cloud.event.EventTypes; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.Service; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.dao.NetworkDao; |
| import com.cloud.network.dao.NetworkVO; |
| import com.cloud.network.element.NetworkACLServiceProvider; |
| import com.cloud.network.element.VpcProvider; |
| import com.cloud.network.vpc.NetworkACLItem.State; |
| import com.cloud.network.vpc.dao.NetworkACLDao; |
| import com.cloud.network.vpc.dao.VpcGatewayDao; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.tags.dao.ResourceTagDao; |
| import com.cloud.user.AccountManager; |
| import com.cloud.utils.component.ManagerBase; |
| import com.cloud.utils.db.DB; |
| import com.cloud.utils.db.EntityManager; |
| import com.cloud.utils.db.Transaction; |
| import com.cloud.utils.db.TransactionCallback; |
| import com.cloud.utils.db.TransactionStatus; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.framework.messagebus.MessageBus; |
| import org.apache.cloudstack.framework.messagebus.PublishScope; |
| import org.apache.log4j.Logger; |
| |
| public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLManager { |
| private static final Logger s_logger = Logger.getLogger(NetworkACLManagerImpl.class); |
| |
| @Inject |
| AccountManager _accountMgr; |
| @Inject |
| NetworkModel _networkMgr; |
| @Inject |
| VpcManager _vpcMgr; |
| @Inject |
| ResourceTagDao _resourceTagDao; |
| @Inject |
| NetworkACLDao _networkACLDao; |
| @Inject |
| NetworkACLItemDao _networkACLItemDao; |
| List<NetworkACLServiceProvider> _networkAclElements; |
| @Inject |
| NetworkModel _networkModel; |
| @Inject |
| NetworkDao _networkDao; |
| @Inject |
| VpcGatewayDao _vpcGatewayDao; |
| @Inject |
| NetworkModel _ntwkModel; |
| @Inject |
| ConfigurationManager _configMgr; |
| @Inject |
| EntityManager _entityMgr; |
| @Inject |
| VpcService _vpcSvc; |
| @Inject |
| MessageBus _messageBus; |
| |
| @Override |
| public NetworkACL createNetworkACL(final String name, final String description, final long vpcId, final Boolean forDisplay) { |
| final NetworkACLVO acl = new NetworkACLVO(name, description, vpcId); |
| if (forDisplay != null) { |
| acl.setDisplay(forDisplay); |
| } |
| return _networkACLDao.persist(acl); |
| } |
| |
| @Override |
| public boolean applyNetworkACL(final long aclId) throws ResourceUnavailableException { |
| boolean handled = true; |
| boolean aclApplyStatus = true; |
| |
| final List<NetworkACLItemVO> rules = _networkACLItemDao.listByACL(aclId); |
| //Find all networks using this ACL and apply the ACL |
| final List<NetworkVO> networks = _networkDao.listByAclId(aclId); |
| for (final NetworkVO network : networks) { |
| if (!applyACLItemsToNetwork(network.getId(), rules)) { |
| handled = false; |
| break; |
| } |
| } |
| |
| final List<VpcGatewayVO> vpcGateways = _vpcGatewayDao.listByAclIdAndType(aclId, VpcGateway.Type.Private); |
| for (final VpcGatewayVO vpcGateway : vpcGateways) { |
| final PrivateGateway privateGateway = _vpcSvc.getVpcPrivateGateway(vpcGateway.getId()); |
| |
| if (!applyACLToPrivateGw(privateGateway)) { |
| aclApplyStatus = false; |
| s_logger.debug("failed to apply network acl item on private gateway " + privateGateway.getId() + "acl id " + aclId); |
| break; |
| } |
| } |
| |
| if (handled && aclApplyStatus) { |
| for (final NetworkACLItem rule : rules) { |
| if (rule.getState() == NetworkACLItem.State.Revoke) { |
| removeRule(rule); |
| } else if (rule.getState() == NetworkACLItem.State.Add) { |
| final NetworkACLItemVO ruleVO = _networkACLItemDao.findById(rule.getId()); |
| ruleVO.setState(NetworkACLItem.State.Active); |
| _networkACLItemDao.update(ruleVO.getId(), ruleVO); |
| } |
| } |
| } |
| return handled && aclApplyStatus; |
| } |
| |
| @Override |
| public NetworkACL getNetworkACL(final long id) { |
| return _networkACLDao.findById(id); |
| } |
| |
| @Override |
| public boolean deleteNetworkACL(final NetworkACL acl) { |
| final long aclId = acl.getId(); |
| final List<NetworkVO> networks = _networkDao.listByAclId(aclId); |
| if (networks != null && networks.size() > 0) { |
| throw new CloudRuntimeException("ACL is still associated with " + networks.size() + " tier(s). Cannot delete network ACL: " + acl.getUuid()); |
| } |
| |
| final List<VpcGatewayVO> pvtGateways = _vpcGatewayDao.listByAclIdAndType(aclId, VpcGateway.Type.Private); |
| |
| if (pvtGateways != null && pvtGateways.size() > 0) { |
| throw new CloudRuntimeException("ACL is still associated with " + pvtGateways.size() + " private gateway(s). Cannot delete network ACL: " + acl.getUuid()); |
| } |
| |
| final List<NetworkACLItemVO> aclItems = _networkACLItemDao.listByACL(aclId); |
| for (final NetworkACLItemVO networkACLItem : aclItems) { |
| revokeNetworkACLItem(networkACLItem.getId()); |
| } |
| |
| return _networkACLDao.remove(aclId); |
| } |
| |
| @Override |
| public boolean replaceNetworkACLForPrivateGw(final NetworkACL acl, final PrivateGateway gateway) throws ResourceUnavailableException { |
| final VpcGatewayVO vpcGatewayVo = _vpcGatewayDao.findById(gateway.getId()); |
| final List<NetworkACLItemVO> aclItems = _networkACLItemDao.listByACL(acl.getId()); |
| if (aclItems == null || aclItems.isEmpty()) { |
| //Revoke ACL Items of the existing ACL if the new network acl is empty |
| //Other wise existing rules will not be removed on the router elelment |
| s_logger.debug("New network ACL is empty. Revoke existing rules before applying ACL"); |
| if (!revokeACLItemsForPrivateGw(gateway)) { |
| throw new CloudRuntimeException("Failed to replace network ACL. Error while removing existing ACL " + "items for privatewa gateway: " + gateway.getId()); |
| } |
| } |
| |
| vpcGatewayVo.setNetworkACLId(acl.getId()); |
| if (_vpcGatewayDao.update(vpcGatewayVo.getId(), vpcGatewayVo)) { |
| return applyACLToPrivateGw(gateway); |
| |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean replaceNetworkACL(final NetworkACL acl, final NetworkVO network) throws ResourceUnavailableException { |
| |
| final NetworkOffering guestNtwkOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); |
| |
| if (guestNtwkOff == null) { |
| throw new InvalidParameterValueException("Can't find network offering associated with network: " + network.getUuid()); |
| } |
| |
| //verify that ACLProvider is supported by network offering |
| if (!_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL)) { |
| throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service"); |
| } |
| |
| if (network.getNetworkACLId() != null) { |
| //Revoke ACL Items of the existing ACL if the new ACL is empty |
| //Existing rules won't be removed otherwise |
| final List<NetworkACLItemVO> aclItems = _networkACLItemDao.listByACL(acl.getId()); |
| if (aclItems == null || aclItems.isEmpty()) { |
| s_logger.debug("New network ACL is empty. Revoke existing rules before applying ACL"); |
| if (!revokeACLItemsForNetwork(network.getId())) { |
| throw new CloudRuntimeException("Failed to replace network ACL. Error while removing existing ACL items for network: " + network.getId()); |
| } |
| } |
| } |
| |
| network.setNetworkACLId(acl.getId()); |
| //Update Network ACL |
| if (_networkDao.update(network.getId(), network)) { |
| s_logger.debug("Updated network: " + network.getId() + " with Network ACL Id: " + acl.getId() + ", Applying ACL items"); |
| //Apply ACL to network |
| final Boolean result = applyACLToNetwork(network.getId()); |
| if (result) { |
| // public message on message bus, so that network elements implementing distributed routing capability |
| // can act on the event |
| _messageBus.publish(_name, "Network_ACL_Replaced", PublishScope.LOCAL, network); |
| } |
| return result; |
| } |
| return false; |
| } |
| |
| @Override |
| @DB |
| @ActionEvent(eventType = EventTypes.EVENT_NETWORK_ACL_ITEM_CREATE, eventDescription = "creating network ACL Item", create = true) |
| public NetworkACLItem createNetworkACLItem(final Integer portStart, final Integer portEnd, final String protocol, final List<String> sourceCidrList, final Integer icmpCode, |
| final Integer icmpType, final NetworkACLItem.TrafficType trafficType, final Long aclId, final String action, Integer number, final Boolean forDisplay) { |
| // If number is null, set it to currentMax + 1 (for backward compatibility) |
| if (number == null) { |
| number = _networkACLItemDao.getMaxNumberByACL(aclId) + 1; |
| } |
| |
| final Integer numberFinal = number; |
| final NetworkACLItemVO newRule = Transaction.execute(new TransactionCallback<NetworkACLItemVO>() { |
| @Override |
| public NetworkACLItemVO doInTransaction(final TransactionStatus status) { |
| NetworkACLItem.Action ruleAction = NetworkACLItem.Action.Allow; |
| if ("deny".equalsIgnoreCase(action)) { |
| ruleAction = NetworkACLItem.Action.Deny; |
| } |
| |
| NetworkACLItemVO newRule = |
| new NetworkACLItemVO(portStart, portEnd, protocol.toLowerCase(), aclId, sourceCidrList, icmpCode, icmpType, trafficType, ruleAction, numberFinal); |
| |
| if (forDisplay != null) { |
| newRule.setDisplay(forDisplay); |
| } |
| |
| newRule = _networkACLItemDao.persist(newRule); |
| |
| if (!_networkACLItemDao.setStateToAdd(newRule)) { |
| throw new CloudRuntimeException("Unable to update the state to add for " + newRule); |
| } |
| CallContext.current().setEventDetails("ACL Item Id: " + newRule.getId()); |
| |
| return newRule; |
| } |
| }); |
| |
| return getNetworkACLItem(newRule.getId()); |
| } |
| |
| @Override |
| public NetworkACLItem getNetworkACLItem(final long ruleId) { |
| return _networkACLItemDao.findById(ruleId); |
| } |
| |
| @Override |
| public boolean revokeNetworkACLItem(final long ruleId) { |
| |
| final NetworkACLItemVO rule = _networkACLItemDao.findById(ruleId); |
| |
| revokeRule(rule); |
| |
| boolean success = false; |
| |
| try { |
| applyNetworkACL(rule.getAclId()); |
| success = true; |
| } catch (final ResourceUnavailableException e) { |
| return false; |
| } |
| |
| return success; |
| } |
| |
| @DB |
| private void revokeRule(final NetworkACLItemVO rule) { |
| if (rule.getState() == State.Staged) { |
| if (s_logger.isDebugEnabled()) { |
| s_logger.debug("Found a rule that is still in stage state so just removing it: " + rule); |
| } |
| _networkACLItemDao.remove(rule.getId()); |
| } else if (rule.getState() == State.Add || rule.getState() == State.Active) { |
| rule.setState(State.Revoke); |
| _networkACLItemDao.update(rule.getId(), rule); |
| } |
| } |
| |
| @Override |
| public boolean revokeACLItemsForNetwork(final long networkId) throws ResourceUnavailableException { |
| final Network network = _networkDao.findById(networkId); |
| if (network.getNetworkACLId() == null) { |
| return true; |
| } |
| final List<NetworkACLItemVO> aclItems = _networkACLItemDao.listByACL(network.getNetworkACLId()); |
| if (aclItems.isEmpty()) { |
| s_logger.debug("Found no network ACL Items for network id=" + networkId); |
| return true; |
| } |
| |
| if (s_logger.isDebugEnabled()) { |
| s_logger.debug("Releasing " + aclItems.size() + " Network ACL Items for network id=" + networkId); |
| } |
| |
| for (final NetworkACLItemVO aclItem : aclItems) { |
| // Mark all Network ACLs rules as Revoke, but don't update in DB |
| if (aclItem.getState() == State.Add || aclItem.getState() == State.Active) { |
| aclItem.setState(State.Revoke); |
| } |
| } |
| |
| final boolean success = applyACLItemsToNetwork(network.getId(), aclItems); |
| |
| if (s_logger.isDebugEnabled() && success) { |
| s_logger.debug("Successfully released Network ACLs for network id=" + networkId + " and # of rules now = " + aclItems.size()); |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public boolean revokeACLItemsForPrivateGw(final PrivateGateway gateway) throws ResourceUnavailableException { |
| final long networkACLId = gateway.getNetworkACLId(); |
| final List<NetworkACLItemVO> aclItems = _networkACLItemDao.listByACL(networkACLId); |
| if (aclItems.isEmpty()) { |
| s_logger.debug("Found no network ACL Items for private gateway 'id=" + gateway.getId() + "'"); |
| return true; |
| } |
| |
| if (s_logger.isDebugEnabled()) { |
| s_logger.debug("Releasing " + aclItems.size() + " Network ACL Items for private gateway id=" + gateway.getId()); |
| } |
| |
| for (final NetworkACLItemVO aclItem : aclItems) { |
| // Mark all Network ACLs rules as Revoke, but don't update in DB |
| if (aclItem.getState() == State.Add || aclItem.getState() == State.Active) { |
| aclItem.setState(State.Revoke); |
| } |
| } |
| |
| final boolean success = applyACLToPrivateGw(gateway, aclItems); |
| |
| if (s_logger.isDebugEnabled() && success) { |
| s_logger.debug("Successfully released Network ACLs for private gateway id=" + gateway.getId() + " and # of rules now = " + aclItems.size()); |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public List<NetworkACLItemVO> listNetworkACLItems(final long guestNtwkId) { |
| final Network network = _networkMgr.getNetwork(guestNtwkId); |
| if (network.getNetworkACLId() == null) { |
| return null; |
| } |
| return _networkACLItemDao.listByACL(network.getNetworkACLId()); |
| } |
| |
| private void removeRule(final NetworkACLItem rule) { |
| //remove the rule |
| _networkACLItemDao.remove(rule.getId()); |
| } |
| |
| @Override |
| public boolean applyACLToPrivateGw(final PrivateGateway gateway) throws ResourceUnavailableException { |
| final VpcGatewayVO vpcGatewayVO = _vpcGatewayDao.findById(gateway.getId()); |
| final List<? extends NetworkACLItem> rules = _networkACLItemDao.listByACL(vpcGatewayVO.getNetworkACLId()); |
| return applyACLToPrivateGw(gateway, rules); |
| } |
| |
| private boolean applyACLToPrivateGw(final PrivateGateway gateway, final List<? extends NetworkACLItem> rules) throws ResourceUnavailableException { |
| List<VpcProvider> vpcElements = null; |
| vpcElements = new ArrayList<VpcProvider>(); |
| vpcElements.add((VpcProvider)_ntwkModel.getElementImplementingProvider(Network.Provider.VPCVirtualRouter.getName())); |
| |
| if (vpcElements == null) { |
| throw new CloudRuntimeException("Failed to initialize vpc elements"); |
| } |
| |
| try{ |
| for (final VpcProvider provider : vpcElements) { |
| return provider.applyACLItemsToPrivateGw(gateway, rules); |
| } |
| } catch(final Exception ex) { |
| s_logger.debug("Failed to apply acl to private gateway " + gateway); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean applyACLToNetwork(final long networkId) throws ResourceUnavailableException { |
| final Network network = _networkDao.findById(networkId); |
| if (network.getNetworkACLId() == null) { |
| return true; |
| } |
| final List<NetworkACLItemVO> rules = _networkACLItemDao.listByACL(network.getNetworkACLId()); |
| return applyACLItemsToNetwork(networkId, rules); |
| } |
| |
| @Override |
| public NetworkACLItem updateNetworkACLItem(final Long id, final String protocol, final List<String> sourceCidrList, final NetworkACLItem.TrafficType trafficType, final String action, |
| final Integer number, final Integer sourcePortStart, final Integer sourcePortEnd, final Integer icmpCode, final Integer icmpType, final String customId, final Boolean forDisplay) throws ResourceUnavailableException { |
| final NetworkACLItemVO aclItem = _networkACLItemDao.findById(id); |
| aclItem.setState(State.Add); |
| |
| if (protocol != null) { |
| aclItem.setProtocol(protocol); |
| } |
| |
| if (sourceCidrList != null) { |
| aclItem.setSourceCidrList(sourceCidrList); |
| } |
| |
| if (trafficType != null) { |
| aclItem.setTrafficType(trafficType); |
| } |
| |
| if (action != null) { |
| NetworkACLItem.Action ruleAction = NetworkACLItem.Action.Allow; |
| if ("deny".equalsIgnoreCase(action)) { |
| ruleAction = NetworkACLItem.Action.Deny; |
| } |
| aclItem.setAction(ruleAction); |
| } |
| |
| if (number != null) { |
| aclItem.setNumber(number); |
| } |
| |
| if (sourcePortStart != null) { |
| aclItem.setSourcePortStart(sourcePortStart); |
| } |
| |
| if (sourcePortEnd != null) { |
| aclItem.setSourcePortEnd(sourcePortEnd); |
| } |
| |
| if (icmpCode != null) { |
| aclItem.setIcmpCode(icmpCode); |
| } |
| |
| if (icmpType != null) { |
| aclItem.setIcmpType(icmpType); |
| } |
| |
| if (customId != null) { |
| aclItem.setUuid(customId); |
| } |
| |
| if (forDisplay != null) { |
| aclItem.setDisplay(forDisplay); |
| } |
| |
| if (_networkACLItemDao.update(id, aclItem)) { |
| if (applyNetworkACL(aclItem.getAclId())) { |
| return aclItem; |
| } else { |
| throw new CloudRuntimeException("Failed to apply Network ACL Item: " + aclItem.getUuid()); |
| } |
| } |
| return null; |
| } |
| |
| public boolean applyACLItemsToNetwork(final long networkId, final List<NetworkACLItemVO> rules) throws ResourceUnavailableException { |
| final Network network = _networkDao.findById(networkId); |
| boolean handled = false; |
| boolean foundProvider = false; |
| for (final NetworkACLServiceProvider element : _networkAclElements) { |
| final Network.Provider provider = element.getProvider(); |
| final boolean isAclProvider = _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.NetworkACL, provider); |
| if (!isAclProvider) { |
| continue; |
| } |
| foundProvider = true; |
| s_logger.debug("Applying NetworkACL for network: " + network.getId() + " with Network ACL service provider"); |
| handled = element.applyNetworkACLs(network, rules); |
| if (handled) { |
| // publish message on message bus, so that network elements implementing distributed routing |
| // capability can act on the event |
| _messageBus.publish(_name, "Network_ACL_Replaced", PublishScope.LOCAL, network); |
| break; |
| } |
| } |
| if (!foundProvider) { |
| s_logger.debug("Unable to find NetworkACL service provider for network: " + network.getId()); |
| } |
| return handled; |
| } |
| |
| public List<NetworkACLServiceProvider> getNetworkAclElements() { |
| return _networkAclElements; |
| } |
| |
| @Inject |
| public void setNetworkAclElements(final List<NetworkACLServiceProvider> networkAclElements) { |
| _networkAclElements = networkAclElements; |
| } |
| |
| } |