| // 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.element; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import javax.inject.Inject; |
| import javax.naming.ConfigurationException; |
| import javax.persistence.EntityExistsException; |
| |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; |
| import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice; |
| import org.apache.log4j.Logger; |
| |
| import com.cloud.agent.AgentManager; |
| import com.cloud.agent.api.Answer; |
| import com.cloud.agent.api.AssociateAsaWithLogicalEdgeFirewallCommand; |
| import com.cloud.agent.api.CleanupLogicalEdgeFirewallCommand; |
| import com.cloud.agent.api.ConfigureNexusVsmForAsaCommand; |
| import com.cloud.agent.api.CreateLogicalEdgeFirewallCommand; |
| import com.cloud.agent.api.StartupCommand; |
| import com.cloud.agent.api.StartupExternalFirewallCommand; |
| import com.cloud.agent.api.routing.NetworkElementCommand; |
| import com.cloud.agent.api.routing.SetFirewallRulesCommand; |
| import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; |
| import com.cloud.agent.api.routing.SetSourceNatCommand; |
| import com.cloud.agent.api.routing.SetStaticNatRulesCommand; |
| import com.cloud.agent.api.to.FirewallRuleTO; |
| import com.cloud.agent.api.to.IpAddressTO; |
| import com.cloud.agent.api.to.PortForwardingRuleTO; |
| import com.cloud.agent.api.to.StaticNatRuleTO; |
| import com.cloud.api.commands.AddCiscoAsa1000vResourceCmd; |
| import com.cloud.api.commands.AddCiscoVnmcResourceCmd; |
| import com.cloud.api.commands.DeleteCiscoAsa1000vResourceCmd; |
| import com.cloud.api.commands.DeleteCiscoVnmcResourceCmd; |
| import com.cloud.api.commands.ListCiscoAsa1000vResourcesCmd; |
| import com.cloud.api.commands.ListCiscoVnmcResourcesCmd; |
| import com.cloud.api.response.CiscoAsa1000vResourceResponse; |
| import com.cloud.api.response.CiscoVnmcResourceResponse; |
| import com.cloud.configuration.ConfigurationManager; |
| import com.cloud.dc.ClusterVO; |
| import com.cloud.dc.ClusterVSMMapVO; |
| import com.cloud.dc.DataCenter; |
| import com.cloud.dc.DataCenter.NetworkType; |
| import com.cloud.dc.Vlan; |
| import com.cloud.dc.VlanVO; |
| import com.cloud.dc.dao.ClusterDao; |
| import com.cloud.dc.dao.ClusterVSMMapDao; |
| import com.cloud.dc.dao.VlanDao; |
| import com.cloud.deploy.DeployDestination; |
| import com.cloud.exception.ConcurrentOperationException; |
| import com.cloud.exception.InsufficientAddressCapacityException; |
| import com.cloud.exception.InsufficientCapacityException; |
| import com.cloud.exception.InvalidParameterValueException; |
| import com.cloud.exception.ResourceAllocationException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.host.DetailVO; |
| import com.cloud.host.Host; |
| import com.cloud.host.HostVO; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.host.dao.HostDetailsDao; |
| import com.cloud.network.CiscoNexusVSMDeviceVO; |
| import com.cloud.network.IpAddress; |
| import com.cloud.network.IpAddressManager; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.Capability; |
| import com.cloud.network.Network.Provider; |
| import com.cloud.network.Network.Service; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.Networks.BroadcastDomainType; |
| import com.cloud.network.PhysicalNetworkServiceProvider; |
| import com.cloud.network.PublicIpAddress; |
| import com.cloud.network.addr.PublicIp; |
| import com.cloud.network.cisco.CiscoAsa1000vDevice; |
| import com.cloud.network.cisco.CiscoAsa1000vDeviceVO; |
| import com.cloud.network.cisco.CiscoVnmcController; |
| import com.cloud.network.cisco.CiscoVnmcControllerVO; |
| import com.cloud.network.cisco.NetworkAsa1000vMapVO; |
| import com.cloud.network.dao.CiscoAsa1000vDao; |
| import com.cloud.network.dao.CiscoNexusVSMDeviceDao; |
| import com.cloud.network.dao.CiscoVnmcDao; |
| import com.cloud.network.dao.IPAddressDao; |
| import com.cloud.network.dao.IPAddressVO; |
| import com.cloud.network.dao.NetworkAsa1000vMapDao; |
| import com.cloud.network.dao.NetworkDao; |
| import com.cloud.network.dao.PhysicalNetworkDao; |
| import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; |
| import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; |
| import com.cloud.network.dao.PhysicalNetworkVO; |
| import com.cloud.network.resource.CiscoVnmcResource; |
| import com.cloud.network.rules.FirewallRule; |
| import com.cloud.network.rules.FirewallRule.TrafficType; |
| import com.cloud.network.rules.PortForwardingRule; |
| import com.cloud.network.rules.StaticNat; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.resource.ResourceManager; |
| import com.cloud.resource.ResourceState; |
| import com.cloud.resource.ResourceStateAdapter; |
| import com.cloud.resource.ServerResource; |
| import com.cloud.resource.UnableDeleteHostException; |
| import com.cloud.user.Account; |
| import com.cloud.utils.component.AdapterBase; |
| 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 com.cloud.utils.exception.ExceptionUtil; |
| import com.cloud.utils.net.NetUtils; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.ReservationContext; |
| import com.cloud.vm.VirtualMachine.Type; |
| import com.cloud.vm.VirtualMachineProfile; |
| |
| public class CiscoVnmcElement extends AdapterBase implements SourceNatServiceProvider, FirewallServiceProvider, PortForwardingServiceProvider, IpDeployer, |
| StaticNatServiceProvider, ResourceStateAdapter, NetworkElement, CiscoVnmcElementService, CiscoAsa1000vService { |
| private static final Logger s_logger = Logger.getLogger(CiscoVnmcElement.class); |
| private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities(); |
| |
| @Inject |
| EntityManager _entityMgr; |
| @Inject |
| AgentManager _agentMgr; |
| @Inject |
| ResourceManager _resourceMgr; |
| @Inject |
| ConfigurationManager _configMgr; |
| @Inject |
| NetworkOrchestrationService _networkMgr; |
| @Inject |
| NetworkModel _networkModel; |
| @Inject |
| IpAddressManager _ipAddrMgr; |
| |
| @Inject |
| PhysicalNetworkDao _physicalNetworkDao; |
| @Inject |
| PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao; |
| @Inject |
| IPAddressDao _ipAddressDao; |
| @Inject |
| HostDetailsDao _hostDetailsDao; |
| @Inject |
| HostDao _hostDao; |
| @Inject |
| NetworkDao _networkDao; |
| @Inject |
| ClusterDao _clusterDao; |
| @Inject |
| VlanDao _vlanDao; |
| @Inject |
| ClusterVSMMapDao _clusterVsmMapDao; |
| @Inject |
| CiscoNexusVSMDeviceDao _vsmDeviceDao; |
| @Inject |
| CiscoVnmcDao _ciscoVnmcDao; |
| @Inject |
| CiscoAsa1000vDao _ciscoAsa1000vDao; |
| @Inject |
| NetworkAsa1000vMapDao _networkAsa1000vMapDao; |
| |
| protected boolean canHandle(Network network) { |
| if (network.getBroadcastDomainType() != BroadcastDomainType.Vlan) { |
| return false; //TODO: should handle VxLAN as well |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { |
| super.configure(name, params); |
| _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this); |
| return true; |
| } |
| |
| private static Map<Service, Map<Capability, String>> setCapabilities() { |
| Map<Service, Map<Capability, String>> capabilities = new HashMap<Service, Map<Capability, String>>(); |
| capabilities.put(Service.Gateway, null); |
| |
| Map<Capability, String> firewallCapabilities = new HashMap<Capability, String>(); |
| firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); |
| firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress,egress"); |
| firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); |
| firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp"); |
| firewallCapabilities.put(Capability.MultipleIps, "true"); |
| capabilities.put(Service.Firewall, firewallCapabilities); |
| |
| capabilities.put(Service.StaticNat, null); |
| capabilities.put(Service.PortForwarding, null); |
| |
| Map<Capability, String> sourceNatCapabilities = new HashMap<Capability, String>(); |
| sourceNatCapabilities.put(Capability.SupportedSourceNatTypes, "peraccount"); |
| sourceNatCapabilities.put(Capability.RedundantRouter, "false"); //TODO: |
| capabilities.put(Service.SourceNat, sourceNatCapabilities); |
| return capabilities; |
| } |
| |
| @Override |
| public Map<Service, Map<Capability, String>> getCapabilities() { |
| return capabilities; |
| } |
| |
| @Override |
| public Provider getProvider() { |
| return Provider.CiscoVnmc; |
| } |
| |
| private boolean createLogicalEdgeFirewall(long vlanId, String gateway, String gatewayNetmask, String publicIp, String publicNetmask, List<String> publicGateways, |
| long hostId) { |
| CreateLogicalEdgeFirewallCommand cmd = new CreateLogicalEdgeFirewallCommand(vlanId, publicIp, gateway, publicNetmask, gatewayNetmask); |
| for (String publicGateway : publicGateways) { |
| cmd.getPublicGateways().add(publicGateway); |
| } |
| Answer answer = _agentMgr.easySend(hostId, cmd); |
| return answer.getResult(); |
| } |
| |
| private boolean configureNexusVsmForAsa(long vlanId, String gateway, String vsmUsername, String vsmPassword, String vsmIp, String asaInPortProfile, long hostId) { |
| ConfigureNexusVsmForAsaCommand cmd = new ConfigureNexusVsmForAsaCommand(vlanId, gateway, vsmUsername, vsmPassword, vsmIp, asaInPortProfile); |
| Answer answer = _agentMgr.easySend(hostId, cmd); |
| return answer.getResult(); |
| } |
| |
| private boolean configureSourceNat(long vlanId, String guestCidr, PublicIp sourceNatIp, long hostId) { |
| boolean add = (sourceNatIp.getState() == IpAddress.State.Releasing ? false : true); |
| IpAddressTO ip = |
| new IpAddressTO(sourceNatIp.getAccountId(), sourceNatIp.getAddress().addr(), add, false, sourceNatIp.isSourceNat(), sourceNatIp.getVlanTag(), |
| sourceNatIp.getGateway(), sourceNatIp.getNetmask(), sourceNatIp.getMacAddress(), null, sourceNatIp.isOneToOneNat()); |
| boolean addSourceNat = false; |
| if (sourceNatIp.isSourceNat()) { |
| addSourceNat = add; |
| } |
| |
| SetSourceNatCommand cmd = new SetSourceNatCommand(ip, addSourceNat); |
| cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); |
| cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, guestCidr); |
| Answer answer = _agentMgr.easySend(hostId, cmd); |
| return answer.getResult(); |
| } |
| |
| private boolean associateAsaWithLogicalEdgeFirewall(long vlanId, String asaMgmtIp, long hostId) { |
| AssociateAsaWithLogicalEdgeFirewallCommand cmd = new AssociateAsaWithLogicalEdgeFirewallCommand(vlanId, asaMgmtIp); |
| Answer answer = _agentMgr.easySend(hostId, cmd); |
| return answer.getResult(); |
| } |
| |
| @Override |
| public boolean implement(final Network network, final NetworkOffering offering, final DeployDestination dest, final ReservationContext context) |
| throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { |
| final DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); |
| |
| if (zone.getNetworkType() == NetworkType.Basic) { |
| s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic); |
| return false; |
| } |
| |
| if (!canHandle(network)) { |
| return false; |
| } |
| |
| final List<CiscoVnmcControllerVO> devices = _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (devices.isEmpty()) { |
| s_logger.error("No Cisco Vnmc device on network " + network.getName()); |
| return false; |
| } |
| |
| List<CiscoAsa1000vDeviceVO> asaList = _ciscoAsa1000vDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (asaList.isEmpty()) { |
| s_logger.debug("No Cisco ASA 1000v device on network " + network.getName()); |
| return false; |
| } |
| |
| NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (asaForNetwork != null) { |
| s_logger.debug("Cisco ASA 1000v device already associated with network " + network.getName()); |
| return true; |
| } |
| |
| if (!_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.SourceNat, Provider.CiscoVnmc)) { |
| s_logger.error("SourceNat service is not provided by Cisco Vnmc device on network " + network.getName()); |
| return false; |
| } |
| |
| try { |
| // ensure that there is an ASA 1000v assigned to this network |
| CiscoAsa1000vDevice assignedAsa = assignAsa1000vToNetwork(network); |
| if (assignedAsa == null) { |
| s_logger.error("Unable to assign ASA 1000v device to network " + network.getName()); |
| throw new CloudRuntimeException("Unable to assign ASA 1000v device to network " + network.getName()); |
| } |
| |
| ClusterVO asaCluster = _clusterDao.findById(assignedAsa.getClusterId()); |
| ClusterVSMMapVO clusterVsmMap = _clusterVsmMapDao.findByClusterId(assignedAsa.getClusterId()); |
| if (clusterVsmMap == null) { |
| s_logger.error("Vmware cluster " + asaCluster.getName() + " has no Cisco Nexus VSM device associated with it"); |
| throw new CloudRuntimeException("Vmware cluster " + asaCluster.getName() + " has no Cisco Nexus VSM device associated with it"); |
| } |
| |
| CiscoNexusVSMDeviceVO vsmDevice = _vsmDeviceDao.findById(clusterVsmMap.getVsmId()); |
| if (vsmDevice == null) { |
| s_logger.error("Unable to load details of Cisco Nexus VSM device associated with cluster " + asaCluster.getName()); |
| throw new CloudRuntimeException("Unable to load details of Cisco Nexus VSM device associated with cluster " + asaCluster.getName()); |
| } |
| |
| CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0); |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId()); |
| _hostDao.loadDetails(ciscoVnmcHost); |
| Account owner = context.getAccount(); |
| PublicIp sourceNatIp = _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network); |
| long vlanId = Long.parseLong(BroadcastDomainType.getValue(network.getBroadcastUri())); |
| |
| List<VlanVO> vlanVOList = _vlanDao.listVlansByPhysicalNetworkId(network.getPhysicalNetworkId()); |
| List<String> publicGateways = new ArrayList<String>(); |
| for (VlanVO vlanVO : vlanVOList) { |
| publicGateways.add(vlanVO.getVlanGateway()); |
| } |
| |
| // due to VNMC limitation of not allowing source NAT ip as the outside ip of firewall, |
| // an additional public ip needs to acquired for assigning as firewall outside ip. |
| // In case there are already additional ip addresses available (network restart) use one |
| // of them such that it is not the source NAT ip |
| IpAddress outsideIp = null; |
| List<IPAddressVO> publicIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), null); |
| for (IPAddressVO ip : publicIps) { |
| if (!ip.isSourceNat()) { |
| outsideIp = ip; |
| break; |
| } |
| } |
| if (outsideIp == null) { // none available, acquire one |
| try { |
| Account caller = CallContext.current().getCallingAccount(); |
| long callerUserId = CallContext.current().getCallingUserId(); |
| outsideIp = _ipAddrMgr.allocateIp(owner, false, caller, callerUserId, zone, true); |
| } catch (ResourceAllocationException e) { |
| s_logger.error("Unable to allocate additional public Ip address. Exception details " + e); |
| throw new CloudRuntimeException("Unable to allocate additional public Ip address. Exception details " + e); |
| } |
| |
| try { |
| outsideIp = _ipAddrMgr.associateIPToGuestNetwork(outsideIp.getId(), network.getId(), true); |
| } catch (ResourceAllocationException e) { |
| s_logger.error("Unable to assign allocated additional public Ip " + outsideIp.getAddress().addr() + " to network with vlan " + vlanId + |
| ". Exception details " + e); |
| throw new CloudRuntimeException("Unable to assign allocated additional public Ip " + outsideIp.getAddress().addr() + " to network with vlan " + |
| vlanId + ". Exception details " + e); |
| } |
| } |
| |
| // create logical edge firewall in VNMC |
| String gatewayNetmask = NetUtils.getCidrNetmask(network.getCidr()); |
| // due to ASA limitation of allowing single subnet to be assigned to firewall interfaces, |
| // all public ip addresses must be from same subnet, this essentially means single public subnet in zone |
| if (!createLogicalEdgeFirewall(vlanId, network.getGateway(), gatewayNetmask, outsideIp.getAddress().addr(), sourceNatIp.getNetmask(), publicGateways, |
| ciscoVnmcHost.getId())) { |
| s_logger.error("Failed to create logical edge firewall in Cisco VNMC device for network " + network.getName()); |
| throw new CloudRuntimeException("Failed to create logical edge firewall in Cisco VNMC device for network " + network.getName()); |
| } |
| |
| // create stuff in VSM for ASA device |
| if (!configureNexusVsmForAsa(vlanId, network.getGateway(), vsmDevice.getUserName(), vsmDevice.getPassword(), vsmDevice.getipaddr(), |
| assignedAsa.getInPortProfile(), ciscoVnmcHost.getId())) { |
| s_logger.error("Failed to configure Cisco Nexus VSM " + vsmDevice.getipaddr() + " for ASA device for network " + network.getName()); |
| throw new CloudRuntimeException("Failed to configure Cisco Nexus VSM " + vsmDevice.getipaddr() + " for ASA device for network " + network.getName()); |
| } |
| |
| // configure source NAT |
| if (!configureSourceNat(vlanId, network.getCidr(), sourceNatIp, ciscoVnmcHost.getId())) { |
| s_logger.error("Failed to configure source NAT in Cisco VNMC device for network " + network.getName()); |
| throw new CloudRuntimeException("Failed to configure source NAT in Cisco VNMC device for network " + network.getName()); |
| } |
| |
| // associate Asa 1000v instance with logical edge firewall |
| if (!associateAsaWithLogicalEdgeFirewall(vlanId, assignedAsa.getManagementIp(), ciscoVnmcHost.getId())) { |
| s_logger.error("Failed to associate Cisco ASA 1000v (" + assignedAsa.getManagementIp() + ") with logical edge firewall in VNMC for network " + |
| network.getName()); |
| throw new CloudRuntimeException("Failed to associate Cisco ASA 1000v (" + assignedAsa.getManagementIp() + |
| ") with logical edge firewall in VNMC for network " + network.getName()); |
| } |
| } catch (CloudRuntimeException e) { |
| unassignAsa1000vFromNetwork(network); |
| s_logger.error("CiscoVnmcElement failed", e); |
| return false; |
| } catch (Exception e) { |
| unassignAsa1000vFromNetwork(network); |
| ExceptionUtil.rethrowRuntime(e); |
| ExceptionUtil.rethrow(e, InsufficientAddressCapacityException.class); |
| ExceptionUtil.rethrow(e, ResourceUnavailableException.class); |
| throw new IllegalStateException(e); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) |
| throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { |
| if (vm.getType() != Type.User) { |
| return false; |
| } |
| |
| // ensure that there is an ASA 1000v assigned to this network |
| NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (asaForNetwork == null) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, |
| ResourceUnavailableException { |
| return true; |
| } |
| |
| private boolean cleanupLogicalEdgeFirewall(long vlanId, long hostId) { |
| CleanupLogicalEdgeFirewallCommand cmd = new CleanupLogicalEdgeFirewallCommand(vlanId); |
| Answer answer = _agentMgr.easySend(hostId, cmd); |
| return answer.getResult(); |
| } |
| |
| @Override |
| public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { |
| |
| unassignAsa1000vFromNetwork(network); |
| |
| long vlanId = Long.parseLong(BroadcastDomainType.getValue(network.getBroadcastUri())); |
| List<CiscoVnmcControllerVO> devices = _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (!devices.isEmpty()) { |
| CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0); |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId()); |
| cleanupLogicalEdgeFirewall(vlanId, ciscoVnmcHost.getId()); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean isReady(PhysicalNetworkServiceProvider provider) { |
| return true; |
| } |
| |
| @Override |
| public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, |
| ResourceUnavailableException { |
| return true; |
| } |
| |
| @Override |
| public boolean canEnableIndividualServices() { |
| return true; |
| } |
| |
| @Override |
| public boolean verifyServicesCombination(Set<Service> services) { |
| if (!services.contains(Service.Firewall)) { |
| s_logger.warn("CiscoVnmc must be used as Firewall Service Provider in the network"); |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { |
| return true; |
| } |
| |
| @Override |
| public List<Class<?>> getCommands() { |
| List<Class<?>> cmdList = new ArrayList<Class<?>>(); |
| cmdList.add(AddCiscoVnmcResourceCmd.class); |
| cmdList.add(DeleteCiscoVnmcResourceCmd.class); |
| cmdList.add(ListCiscoVnmcResourcesCmd.class); |
| cmdList.add(AddCiscoAsa1000vResourceCmd.class); |
| cmdList.add(DeleteCiscoAsa1000vResourceCmd.class); |
| cmdList.add(ListCiscoAsa1000vResourcesCmd.class); |
| return cmdList; |
| } |
| |
| @Override |
| public CiscoVnmcController addCiscoVnmcResource(AddCiscoVnmcResourceCmd cmd) { |
| final String deviceName = Provider.CiscoVnmc.getName(); |
| NetworkDevice networkDevice = NetworkDevice.getNetworkDevice(deviceName); |
| final Long physicalNetworkId = cmd.getPhysicalNetworkId(); |
| CiscoVnmcController ciscoVnmcResource = null; |
| |
| PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); |
| if (physicalNetwork == null) { |
| throw new InvalidParameterValueException("Could not find phyical network with ID: " + physicalNetworkId); |
| } |
| long zoneId = physicalNetwork.getDataCenterId(); |
| |
| final PhysicalNetworkServiceProviderVO ntwkSvcProvider = |
| _physicalNetworkServiceProviderDao.findByServiceProvider(physicalNetwork.getId(), networkDevice.getNetworkServiceProvder()); |
| if (ntwkSvcProvider == null) { |
| throw new CloudRuntimeException("Network Service Provider: " + networkDevice.getNetworkServiceProvder() + " is not enabled in the physical network: " + |
| physicalNetworkId + "to add this device"); |
| } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { |
| throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + " is in shutdown state in the physical network: " + |
| physicalNetworkId + "to add this device"); |
| } |
| |
| if (_ciscoVnmcDao.listByPhysicalNetwork(physicalNetworkId).size() != 0) { |
| throw new CloudRuntimeException("A Cisco Vnmc device is already configured on this physical network"); |
| } |
| |
| Map<String, String> params = new HashMap<String, String>(); |
| params.put("guid", UUID.randomUUID().toString()); |
| params.put("zoneId", String.valueOf(physicalNetwork.getDataCenterId())); |
| params.put("physicalNetworkId", String.valueOf(physicalNetwork.getId())); |
| params.put("name", "Cisco VNMC Controller - " + cmd.getHost()); |
| params.put("ip", cmd.getHost()); |
| params.put("username", cmd.getUsername()); |
| params.put("password", cmd.getPassword()); |
| |
| Map<String, Object> hostdetails = new HashMap<String, Object>(); |
| hostdetails.putAll(params); |
| |
| ServerResource resource = new CiscoVnmcResource(); |
| try { |
| resource.configure(cmd.getHost(), hostdetails); |
| |
| final Host host = _resourceMgr.addHost(zoneId, resource, Host.Type.ExternalFirewall, params); |
| if (host != null) { |
| return Transaction.execute(new TransactionCallback<CiscoVnmcController>() { |
| @Override |
| public CiscoVnmcController doInTransaction(TransactionStatus status) { |
| CiscoVnmcController ciscoVnmcResource = new CiscoVnmcControllerVO(host.getId(), physicalNetworkId, ntwkSvcProvider.getProviderName(), deviceName); |
| _ciscoVnmcDao.persist((CiscoVnmcControllerVO)ciscoVnmcResource); |
| |
| DetailVO detail = new DetailVO(host.getId(), "deviceid", String.valueOf(ciscoVnmcResource.getId())); |
| _hostDetailsDao.persist(detail); |
| |
| return ciscoVnmcResource; |
| } |
| }); |
| } else { |
| throw new CloudRuntimeException("Failed to add Cisco Vnmc device due to internal error."); |
| } |
| } catch (ConfigurationException e) { |
| throw new CloudRuntimeException(e.getMessage()); |
| } |
| } |
| |
| @Override |
| public CiscoVnmcResourceResponse createCiscoVnmcResourceResponse(CiscoVnmcController ciscoVnmcResourceVO) { |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcResourceVO.getHostId()); |
| |
| CiscoVnmcResourceResponse response = new CiscoVnmcResourceResponse(); |
| response.setId(ciscoVnmcResourceVO.getUuid()); |
| response.setPhysicalNetworkId(ciscoVnmcResourceVO.getPhysicalNetworkId()); |
| response.setProviderName(ciscoVnmcResourceVO.getProviderName()); |
| response.setResourceName(ciscoVnmcHost.getName()); |
| |
| return response; |
| } |
| |
| @Override |
| public boolean deleteCiscoVnmcResource(DeleteCiscoVnmcResourceCmd cmd) { |
| Long vnmcResourceId = cmd.getCiscoVnmcResourceId(); |
| CiscoVnmcControllerVO vnmcResource = _ciscoVnmcDao.findById(vnmcResourceId); |
| if (vnmcResource == null) { |
| throw new InvalidParameterValueException("Could not find a Cisco VNMC appliance with id " + vnmcResourceId); |
| } |
| |
| // Check if there any ASA 1000v appliances |
| Long physicalNetworkId = vnmcResource.getPhysicalNetworkId(); |
| PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); |
| if (physicalNetwork != null) { |
| List<CiscoAsa1000vDeviceVO> responseList = _ciscoAsa1000vDao.listByPhysicalNetwork(physicalNetworkId); |
| if (responseList.size() > 0) { |
| throw new CloudRuntimeException("Cisco VNMC appliance with id " + vnmcResourceId + " cannot be deleted as there Cisco ASA 1000v appliances using it"); |
| } |
| } |
| |
| HostVO vnmcHost = _hostDao.findById(vnmcResource.getHostId()); |
| Long hostId = vnmcHost.getId(); |
| vnmcHost.setResourceState(ResourceState.Maintenance); |
| _hostDao.update(hostId, vnmcHost); |
| _resourceMgr.deleteHost(hostId, false, false); |
| _ciscoVnmcDao.remove(vnmcResourceId); |
| |
| return true; |
| } |
| |
| @Override |
| public List<CiscoVnmcControllerVO> listCiscoVnmcResources(ListCiscoVnmcResourcesCmd cmd) { |
| Long physicalNetworkId = cmd.getPhysicalNetworkId(); |
| Long ciscoVnmcResourceId = cmd.getCiscoVnmcResourceId(); |
| List<CiscoVnmcControllerVO> responseList = new ArrayList<CiscoVnmcControllerVO>(); |
| |
| if (physicalNetworkId == null && ciscoVnmcResourceId == null) { |
| throw new InvalidParameterValueException("Either physical network Id or vnmc device Id must be specified"); |
| } |
| |
| if (ciscoVnmcResourceId != null) { |
| CiscoVnmcControllerVO ciscoVnmcResource = _ciscoVnmcDao.findById(ciscoVnmcResourceId); |
| if (ciscoVnmcResource == null) { |
| throw new InvalidParameterValueException("Could not find Cisco Vnmc device with id: " + ciscoVnmcResource); |
| } |
| responseList.add(ciscoVnmcResource); |
| } else { |
| PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); |
| if (physicalNetwork == null) { |
| throw new InvalidParameterValueException("Could not find a physical network with id: " + physicalNetworkId); |
| } |
| responseList = _ciscoVnmcDao.listByPhysicalNetwork(physicalNetworkId); |
| } |
| |
| return responseList; |
| } |
| |
| @Override |
| public IpDeployer getIpDeployer(Network network) { |
| return this; |
| } |
| |
| @Override |
| public boolean applyFWRules(Network network, List<? extends FirewallRule> rules) throws ResourceUnavailableException { |
| |
| if (!_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Firewall, Provider.CiscoVnmc)) { |
| s_logger.error("Firewall service is not provided by Cisco Vnmc device on network " + network.getName()); |
| return false; |
| } |
| |
| // Find VNMC host for physical network |
| List<CiscoVnmcControllerVO> devices = _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (devices.isEmpty()) { |
| s_logger.error("No Cisco Vnmc device on network " + network.getName()); |
| return true; |
| } |
| |
| // Find if ASA 1000v is associated with network |
| NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (asaForNetwork == null) { |
| s_logger.debug("Cisco ASA 1000v device is not associated with network " + network.getName()); |
| return true; |
| } |
| |
| if (network.getState() == Network.State.Allocated) { |
| s_logger.debug("External firewall was asked to apply firewall rules for network with ID " + network.getId() + |
| "; this network is not implemented. Skipping backend commands."); |
| return true; |
| } |
| |
| CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0); |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId()); |
| |
| List<FirewallRuleTO> rulesTO = new ArrayList<FirewallRuleTO>(); |
| for (FirewallRule rule : rules) { |
| String address = "0.0.0.0"; |
| if (rule.getTrafficType() == TrafficType.Ingress) { |
| IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); |
| address = sourceIp.getAddress().addr(); |
| } |
| FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, address, rule.getPurpose(), rule.getTrafficType()); |
| rulesTO.add(ruleTO); |
| } |
| |
| if (!rulesTO.isEmpty()) { |
| SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rulesTO); |
| cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, BroadcastDomainType.getValue(network.getBroadcastUri())); |
| cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr()); |
| Answer answer = _agentMgr.easySend(ciscoVnmcHost.getId(), cmd); |
| if (answer == null || !answer.getResult()) { |
| String details = (answer != null) ? answer.getDetails() : "details unavailable"; |
| String msg = "Unable to apply firewall rules to Cisco ASA 1000v appliance due to: " + details + "."; |
| s_logger.error(msg); |
| throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyPFRules(Network network, List<PortForwardingRule> rules) throws ResourceUnavailableException { |
| |
| if (!_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.PortForwarding, Provider.CiscoVnmc)) { |
| s_logger.error("Port forwarding service is not provided by Cisco Vnmc device on network " + network.getName()); |
| return false; |
| } |
| |
| // Find VNMC host for physical network |
| List<CiscoVnmcControllerVO> devices = _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (devices.isEmpty()) { |
| s_logger.error("No Cisco Vnmc device on network " + network.getName()); |
| return true; |
| } |
| |
| // Find if ASA 1000v is associated with network |
| NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (asaForNetwork == null) { |
| s_logger.debug("Cisco ASA 1000v device is not associated with network " + network.getName()); |
| return true; |
| } |
| |
| if (network.getState() == Network.State.Allocated) { |
| s_logger.debug("External firewall was asked to apply port forwarding rules for network with ID " + network.getId() + |
| "; this network is not implemented. Skipping backend commands."); |
| return true; |
| } |
| |
| CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0); |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId()); |
| |
| List<PortForwardingRuleTO> rulesTO = new ArrayList<PortForwardingRuleTO>(); |
| for (PortForwardingRule rule : rules) { |
| IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); |
| Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); |
| PortForwardingRuleTO ruleTO = new PortForwardingRuleTO(rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); |
| rulesTO.add(ruleTO); |
| } |
| |
| if (!rulesTO.isEmpty()) { |
| SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rulesTO); |
| cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, BroadcastDomainType.getValue(network.getBroadcastUri())); |
| cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr()); |
| Answer answer = _agentMgr.easySend(ciscoVnmcHost.getId(), cmd); |
| if (answer == null || !answer.getResult()) { |
| String details = (answer != null) ? answer.getDetails() : "details unavailable"; |
| String msg = "Unable to apply port forwarding rules to Cisco ASA 1000v appliance due to: " + details + "."; |
| s_logger.error(msg); |
| throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyStaticNats(Network network, List<? extends StaticNat> rules) throws ResourceUnavailableException { |
| if (!_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.StaticNat, Provider.CiscoVnmc)) { |
| s_logger.error("Static NAT service is not provided by Cisco Vnmc device on network " + network.getName()); |
| return false; |
| } |
| |
| // Find VNMC host for physical network |
| List<CiscoVnmcControllerVO> devices = _ciscoVnmcDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| if (devices.isEmpty()) { |
| s_logger.error("No Cisco Vnmc device on network " + network.getName()); |
| return true; |
| } |
| |
| // Find if ASA 1000v is associated with network |
| NetworkAsa1000vMapVO asaForNetwork = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (asaForNetwork == null) { |
| s_logger.debug("Cisco ASA 1000v device is not associated with network " + network.getName()); |
| return true; |
| } |
| |
| if (network.getState() == Network.State.Allocated) { |
| s_logger.debug("External firewall was asked to apply static NAT rules for network with ID " + network.getId() + |
| "; this network is not implemented. Skipping backend commands."); |
| return true; |
| } |
| |
| CiscoVnmcControllerVO ciscoVnmcDevice = devices.get(0); |
| HostVO ciscoVnmcHost = _hostDao.findById(ciscoVnmcDevice.getHostId()); |
| |
| List<StaticNatRuleTO> rulesTO = new ArrayList<StaticNatRuleTO>(); |
| for (StaticNat rule : rules) { |
| IpAddress sourceIp = _networkModel.getIp(rule.getSourceIpAddressId()); |
| StaticNatRuleTO ruleTO = |
| new StaticNatRuleTO(rule.getSourceIpAddressId(), sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(), |
| false); |
| rulesTO.add(ruleTO); |
| } |
| |
| if (!rulesTO.isEmpty()) { |
| SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO, null); |
| cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, BroadcastDomainType.getValue(network.getBroadcastUri())); |
| cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, network.getCidr()); |
| Answer answer = _agentMgr.easySend(ciscoVnmcHost.getId(), cmd); |
| if (answer == null || !answer.getResult()) { |
| String details = (answer != null) ? answer.getDetails() : "details unavailable"; |
| String msg = "Unable to apply static NAT rules to Cisco ASA 1000v appliance due to: " + details + "."; |
| s_logger.error(msg); |
| throw new ResourceUnavailableException(msg, DataCenter.class, network.getDataCenterId()); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean applyIps(Network network, List<? extends PublicIpAddress> ipAddress, Set<Service> services) throws ResourceUnavailableException { |
| // TODO Auto-generated method stub |
| return false; |
| } |
| |
| @Override |
| public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details, List<String> hostTags) { |
| if (!(startup[0] instanceof StartupExternalFirewallCommand)) { |
| return null; |
| } |
| host.setType(Host.Type.ExternalFirewall); |
| return host; |
| } |
| |
| @Override |
| public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { |
| if (host.getType() != com.cloud.host.Host.Type.ExternalFirewall) { |
| return null; |
| } |
| return new DeleteHostAnswer(true); |
| } |
| |
| @Override |
| public CiscoAsa1000vDevice addCiscoAsa1000vResource(AddCiscoAsa1000vResourceCmd cmd) { |
| Long physicalNetworkId = cmd.getPhysicalNetworkId(); |
| CiscoAsa1000vDevice ciscoAsa1000vResource = null; |
| |
| PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); |
| if (physicalNetwork == null) { |
| throw new InvalidParameterValueException("Could not find phyical network with ID: " + physicalNetworkId); |
| } |
| |
| ciscoAsa1000vResource = new CiscoAsa1000vDeviceVO(physicalNetworkId, cmd.getManagementIp().trim(), cmd.getInPortProfile(), cmd.getClusterId()); |
| try { |
| _ciscoAsa1000vDao.persist((CiscoAsa1000vDeviceVO)ciscoAsa1000vResource); |
| } catch (EntityExistsException e) { |
| throw new InvalidParameterValueException("An ASA 1000v appliance already exists with same configuration"); |
| } |
| |
| return ciscoAsa1000vResource; |
| } |
| |
| @Override |
| public CiscoAsa1000vResourceResponse createCiscoAsa1000vResourceResponse(CiscoAsa1000vDevice ciscoAsa1000vDeviceVO) { |
| CiscoAsa1000vResourceResponse response = new CiscoAsa1000vResourceResponse(); |
| response.setId(ciscoAsa1000vDeviceVO.getUuid()); |
| response.setManagementIp(ciscoAsa1000vDeviceVO.getManagementIp()); |
| response.setInPortProfile(ciscoAsa1000vDeviceVO.getInPortProfile()); |
| |
| NetworkAsa1000vMapVO networkAsaMap = _networkAsa1000vMapDao.findByAsa1000vId(ciscoAsa1000vDeviceVO.getId()); |
| if (networkAsaMap != null) { |
| response.setGuestNetworkId(networkAsaMap.getNetworkId()); |
| } |
| |
| return response; |
| } |
| |
| @Override |
| public boolean deleteCiscoAsa1000vResource(DeleteCiscoAsa1000vResourceCmd cmd) { |
| Long asaResourceId = cmd.getCiscoAsa1000vResourceId(); |
| CiscoAsa1000vDeviceVO asaResource = _ciscoAsa1000vDao.findById(asaResourceId); |
| if (asaResource == null) { |
| throw new InvalidParameterValueException("Could not find a Cisco ASA 1000v appliance with id " + asaResourceId); |
| } |
| |
| NetworkAsa1000vMapVO networkAsaMap = _networkAsa1000vMapDao.findByAsa1000vId(asaResource.getId()); |
| if (networkAsaMap != null) { |
| throw new CloudRuntimeException("Cisco ASA 1000v appliance with id " + asaResourceId + " cannot be deleted as it is associated with guest network"); |
| } |
| |
| _ciscoAsa1000vDao.remove(asaResourceId); |
| |
| return true; |
| } |
| |
| @Override |
| public List<CiscoAsa1000vDeviceVO> listCiscoAsa1000vResources(ListCiscoAsa1000vResourcesCmd cmd) { |
| Long physicalNetworkId = cmd.getPhysicalNetworkId(); |
| Long ciscoAsa1000vResourceId = cmd.getCiscoAsa1000vResourceId(); |
| List<CiscoAsa1000vDeviceVO> responseList = new ArrayList<CiscoAsa1000vDeviceVO>(); |
| |
| if (physicalNetworkId == null && ciscoAsa1000vResourceId == null) { |
| throw new InvalidParameterValueException("Either physical network Id or Asa 1000v device Id must be specified"); |
| } |
| |
| if (ciscoAsa1000vResourceId != null) { |
| CiscoAsa1000vDeviceVO ciscoAsa1000vResource = _ciscoAsa1000vDao.findById(ciscoAsa1000vResourceId); |
| if (ciscoAsa1000vResource == null) { |
| throw new InvalidParameterValueException("Could not find Cisco Asa 1000v device with id: " + ciscoAsa1000vResourceId); |
| } |
| responseList.add(ciscoAsa1000vResource); |
| } else { |
| PhysicalNetworkVO physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); |
| if (physicalNetwork == null) { |
| throw new InvalidParameterValueException("Could not find a physical network with id: " + physicalNetworkId); |
| } |
| responseList = _ciscoAsa1000vDao.listByPhysicalNetwork(physicalNetworkId); |
| } |
| |
| return responseList; |
| } |
| |
| @Override |
| public CiscoAsa1000vDevice assignAsa1000vToNetwork(Network network) { |
| List<CiscoAsa1000vDeviceVO> asaList = _ciscoAsa1000vDao.listByPhysicalNetwork(network.getPhysicalNetworkId()); |
| for (CiscoAsa1000vDeviceVO asa : asaList) { |
| NetworkAsa1000vMapVO assignedToNetwork = _networkAsa1000vMapDao.findByAsa1000vId(asa.getId()); |
| if (assignedToNetwork == null) { |
| NetworkAsa1000vMapVO networkAsaMap = new NetworkAsa1000vMapVO(network.getId(), asa.getId()); |
| _networkAsa1000vMapDao.persist(networkAsaMap); |
| return asa; |
| } |
| } |
| return null; |
| } |
| |
| private void unassignAsa1000vFromNetwork(Network network) { |
| NetworkAsa1000vMapVO networkAsaMap = _networkAsa1000vMapDao.findByNetworkId(network.getId()); |
| if (networkAsaMap != null) { |
| _networkAsa1000vMapDao.remove(networkAsaMap.getId()); |
| } |
| } |
| } |