/** * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. * * This software is licensed under the GNU General Public License v3 or later. * * It 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, either version 3 of the License, or any later version. * 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 . * */ package com.cloud.network; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.api.to.NicTO; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; import com.cloud.api.commands.AssociateIPAddrCmd; import com.cloud.api.commands.CreateNetworkCmd; import com.cloud.api.commands.DisassociateIPAddrCmd; import com.cloud.api.commands.ListNetworksCmd; import com.cloud.api.commands.RestartNetworkCmd; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceLimitDao; import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventVO; import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AccountLimitException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network.Capability; import com.cloud.network.Network.GuestIpType; import com.cloud.network.Network.Service; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.element.NetworkElement; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.RulesManager; import com.cloud.network.vpn.PasswordResetElement; import com.cloud.network.vpn.RemoteAccessVpnElement; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Availability; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.UserStatisticsVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; /** * NetworkManagerImpl implements NetworkManager. */ @Local(value = { NetworkManager.class, NetworkService.class }) public class NetworkManagerImpl implements NetworkManager, NetworkService, Manager { private static final Logger s_logger = Logger.getLogger(NetworkManagerImpl.class); String _name; @Inject DataCenterDao _dcDao = null; @Inject VlanDao _vlanDao = null; @Inject IPAddressDao _ipAddressDao = null; @Inject AccountDao _accountDao = null; @Inject DomainDao _domainDao = null; @Inject UserStatisticsDao _userStatsDao = null; @Inject EventDao _eventDao = null; @Inject ConfigurationDao _configDao; @Inject UserVmDao _vmDao = null; @Inject ResourceLimitDao _limitDao = null; @Inject CapacityDao _capacityDao = null; @Inject AlertManager _alertMgr; @Inject AccountManager _accountMgr; @Inject ConfigurationManager _configMgr; @Inject AccountVlanMapDao _accountVlanMapDao; @Inject NetworkOfferingDao _networkOfferingDao = null; @Inject NetworkDao _networksDao = null; @Inject NicDao _nicDao = null; @Inject RulesManager _rulesMgr; @Inject LoadBalancingRulesManager _lbMgr; @Inject UsageEventDao _usageEventDao; @Inject PodVlanMapDao _podVlanMapDao; @Inject(adapter = NetworkGuru.class) Adapters _networkGurus; @Inject(adapter = NetworkElement.class) Adapters _networkElements; private HashMap _systemNetworks = new HashMap(5); ScheduledExecutorService _executor; SearchBuilder AccountsUsingNetworkSearch; SearchBuilder AssignIpAddressSearch; SearchBuilder AssignIpAddressFromPodVlanSearch; SearchBuilder IpAddressSearch; SearchBuilder NicForTrafficTypeSearch; int _networkGcWait; int _networkGcInterval; String _networkDomain; private Map _configs; HashMap _lastNetworkIdsToFree = new HashMap(); @Override public PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId) throws InsufficientAddressCapacityException { return fetchNewPublicIp(dcId, podId, null, owner, type, networkId, false, true); } @DB public PublicIp fetchNewPublicIp(long dcId, Long podId, Long vlanDbId, Account owner, VlanType vlanUse, Long networkId, boolean sourceNat, boolean assign) throws InsufficientAddressCapacityException { Transaction txn = Transaction.currentTxn(); txn.start(); SearchCriteria sc = null; if (podId != null) { sc = AssignIpAddressFromPodVlanSearch.create(); sc.setJoinParameters("podVlanMapSB", "podId", podId); } else { sc = AssignIpAddressSearch.create(); } if (vlanDbId != null) { sc.addAnd("vlanId", SearchCriteria.Op.EQ, vlanDbId); } sc.setParameters("dc", dcId); // for direct network take ip addresses only from the vlans belonging to the network if (vlanUse == VlanType.DirectAttached) { sc.setJoinParameters("vlan", "networkId", networkId); } sc.setJoinParameters("vlan", "type", vlanUse); Filter filter = new Filter(IPAddressVO.class, "vlanId", true, 0l, 1l); List addrs = _ipAddressDao.lockRows(sc, filter, true); if (addrs.size() == 0) { throw new InsufficientAddressCapacityException("Insufficient address capacity", DataCenter.class, dcId); } assert (addrs.size() == 1) : "Return size is incorrect: " + addrs.size(); IPAddressVO addr = addrs.get(0); addr.setSourceNat(sourceNat); addr.setAllocatedTime(new Date()); addr.setAllocatedInDomainId(owner.getDomainId()); addr.setAllocatedToAccountId(owner.getId()); addr.setState(assign ? IpAddress.State.Allocated : IpAddress.State.Allocating); if (vlanUse == VlanType.DirectAttached) { addr.setState(IpAddress.State.Allocated); } else { addr.setAssociatedWithNetworkId(networkId); } if (!_ipAddressDao.update(addr.getAddress(), addr)) { throw new CloudRuntimeException("Found address to allocate but unable to update: " + addr); } if(owner.getAccountId() != Account.ACCOUNT_ID_SYSTEM){ long isSourceNat = (sourceNat) ? 1 : 0 ; UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_IP_ASSIGN, owner.getAccountId(), dcId, isSourceNat, addr.getAddress().toString()); _usageEventDao.persist(usageEvent); } txn.commit(); long macAddress = NetUtils.createSequenceBasedMacAddress(addr.getMacAddress()); return new PublicIp(addr, _vlanDao.findById(addr.getVlanId()), macAddress); } @Override @DB public PublicIp assignSourceNatIpAddress(Account owner, Network network, long callerId) throws ConcurrentOperationException, InsufficientAddressCapacityException { assert (network.getTrafficType() != null) : "You're asking for a source nat but your network can't participate in source nat. What do you have to say for yourself?"; long dcId = network.getDataCenterId(); long ownerId = owner.getId(); PublicIp ip = null; Transaction txn = Transaction.currentTxn(); try { txn.start(); owner = _accountDao.acquireInLockTable(ownerId); if (owner == null) { throw new ConcurrentOperationException("Unable to lock account " + ownerId); } if (s_logger.isDebugEnabled()) { s_logger.debug("lock account " + ownerId + " is acquired"); } IPAddressVO sourceNat = null; List addrs = listPublicIpAddressesInVirtualNetwork(ownerId, dcId, null); if (addrs.size() == 0) { // Check that the maximum number of public IPs for the given accountId will not be exceeded if (_accountMgr.resourceLimitExceeded(owner, ResourceType.public_ip)) { throw new AccountLimitException("Maximum number of public IP addresses for account: " + owner.getAccountName() + " has been exceeded."); } if (s_logger.isDebugEnabled()) { s_logger.debug("assigning a new ip address in " + dcId + " to " + owner); } //If account has Account specific ip ranges, try to allocate ip from there Long vlanId = null; List maps = _accountVlanMapDao.listAccountVlanMapsByAccount(ownerId); if (maps != null && !maps.isEmpty()) { vlanId = maps.get(0).getVlanDbId(); } ip = fetchNewPublicIp(dcId, null, vlanId, owner, VlanType.VirtualNetwork, network.getId(), true, false); sourceNat = ip.ip(); sourceNat.setState(IpAddress.State.Allocated); _ipAddressDao.update(sourceNat.getAddress(), sourceNat); // Increment the number of public IPs for this accountId in the database _accountMgr.incrementResourceCount(ownerId, ResourceType.public_ip); } else { // Account already has ip addresses for (IPAddressVO addr : addrs) { if (addr.isSourceNat()) { sourceNat = addr; break; } } assert (sourceNat != null) : "How do we get a bunch of ip addresses but none of them are source nat? account=" + ownerId + "; dc=" + dcId; ip = new PublicIp(sourceNat, _vlanDao.findById(sourceNat.getVlanId()), NetUtils.createSequenceBasedMacAddress(sourceNat.getMacAddress())); } UserStatisticsVO stats = _userStatsDao.findBy(ownerId, dcId, null, null); if (stats == null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating statistics for the owner: " + ownerId); } stats = new UserStatisticsVO(ownerId, dcId, null, null); _userStatsDao.persist(stats); } txn.commit(); return ip; } finally { if (owner != null) { if (s_logger.isDebugEnabled()) { s_logger.debug("Releasing lock account " + ownerId); } _accountDao.releaseFromLockTable(ownerId); } if (ip == null) { txn.rollback(); s_logger.error("Unable to get source nat ip address for account " + ownerId); } } } /** * Returns the target account for an api command * * @param accountName * - non-null if the account name was passed in in the command * @param domainId * - non-null if the domainId was passed in in the command. * @return */ protected Account getAccountForApiCommand(String accountName, Long domainId) throws InvalidParameterValueException, PermissionDeniedException { Account account = UserContext.current().getCaller(); if (_accountMgr.isAdmin(account.getType())) { // The admin is making the call, determine if it is for someone else or for himself if (domainId != null) { if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) { throw new PermissionDeniedException("Invalid domain id (" + domainId + ") given, , permission denied"); } if (accountName != null) { Account userAccount = _accountMgr.getActiveAccount(accountName, domainId); if (userAccount != null) { account = userAccount; } else { throw new PermissionDeniedException("Unable to find account " + accountName + " in domain " + domainId + ", permission denied"); } } } else { // the admin is calling the api on his own behalf return account; } } return account; } public boolean applyIpAssociations(Network network, boolean continueOnError) throws ResourceUnavailableException { List userIps = _ipAddressDao.listByAssociatedNetwork(network.getId()); List publicIps = new ArrayList(); if (userIps != null && !userIps.isEmpty()) { for (IPAddressVO userIp : userIps) { PublicIp publicIp = new PublicIp(userIp, _vlanDao.findById(userIp.getVlanId()), userIp.getMacAddress()); publicIps.add(publicIp); } } boolean success = true; for (NetworkElement element : _networkElements) { try { element.applyIps(network, publicIps); } catch (ResourceUnavailableException e) { success = false; if (!continueOnError) { throw e; } else { s_logger.debug("Resource is not available: " + element.getName(), e); } } } if (success) { for (IPAddressVO addr : userIps) { if (addr.getState() == IpAddress.State.Allocating) { addr.setState(IpAddress.State.Allocated); addr.setAssociatedWithNetworkId(network.getId()); _ipAddressDao.update(addr.getAddress(), addr); } else if (addr.getState() == IpAddress.State.Releasing) { _ipAddressDao.unassignIpAddress(addr.getAddress()); } } } return success; } @Override public List getVirtualNetworksOwnedByAccountInZone(String accountName, long domainId, long zoneId) { Account owner = _accountMgr.getActiveAccount(accountName, domainId); if (owner == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId + ", permission denied"); } return _networksDao.listBy(owner.getId(), zoneId, GuestIpType.Virtual); } @Override @DB public IpAddress associateIP(AssociateIPAddrCmd cmd) throws ResourceAllocationException, ResourceUnavailableException, InsufficientAddressCapacityException, ConcurrentOperationException { String accountName = cmd.getAccountName(); long domainId = cmd.getDomainId(); Long zoneId = cmd.getZoneId(); Account caller = UserContext.current().getCaller(); long userId = UserContext.current().getCallerUserId(); Account owner = _accountMgr.getActiveAccount(accountName, domainId); if (owner == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId + ", permission denied"); } _accountMgr.checkAccess(caller, owner); long ownerId = owner.getId(); Long networkId = cmd.getNetworkId(); Network network = null; if (networkId != null) { network = _networksDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("Network id is invalid: " + networkId); } } PublicIp ip = null; boolean success = false; Transaction txn = Transaction.currentTxn(); Account accountToLock = null; try { if (s_logger.isDebugEnabled()) { s_logger.debug("Associate IP address called for user " + userId + " account " + ownerId); } accountToLock = _accountDao.acquireInLockTable(ownerId); if (accountToLock == null) { s_logger.warn("Unable to lock account: " + ownerId); throw new ConcurrentOperationException("Unable to acquire account lock"); } if (s_logger.isDebugEnabled()) { s_logger.debug("Associate IP address lock acquired"); } // Check that the maximum number of public IPs for the given // accountId will not be exceeded if (_accountMgr.resourceLimitExceeded(accountToLock, ResourceType.public_ip)) { ResourceAllocationException rae = new ResourceAllocationException("Maximum number of public IP addresses for account: " + accountToLock.getAccountName() + " has been exceeded."); rae.setResourceType("ip"); throw rae; } txn.start(); ip = fetchNewPublicIp(zoneId, null, null, owner, VlanType.VirtualNetwork, network.getId(), false, false); if (ip == null) { throw new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zoneId); } _accountMgr.incrementResourceCount(ownerId, ResourceType.public_ip); Ip ipAddress = ip.getAddress(); s_logger.debug("Got " + ipAddress + " to assign for account " + owner.getId() + " in zone " + network.getDataCenterId()); txn.commit(); success = applyIpAssociations(network, false); if (success) { s_logger.debug("Successfully associated ip address " + ip + " for account " + owner.getId() + " in zone " + network.getDataCenterId()); } else { s_logger.warn("Failed to associate ip address " + ip + " for account " + owner.getId() + " in zone " + network.getDataCenterId()); } return ip; } catch (ResourceUnavailableException e) { s_logger.error("Unable to associate ip address due to resource unavailable exception", e); return null; } finally { if (accountToLock != null) { _accountDao.releaseFromLockTable(ownerId); s_logger.debug("Associate IP address lock released"); } if (!success) { if (ip != null) { try { s_logger.warn("Failed to associate ip address " + ip); _ipAddressDao.markAsUnavailable(ip.getAddress(), ip.getAccountId()); applyIpAssociations(network, true); } catch (Exception e) { s_logger.warn("Unable to disassociate ip address for recovery", e); } txn.start(); _ipAddressDao.unassignIpAddress(ip.getAddress()); _accountMgr.decrementResourceCount(ownerId, ResourceType.public_ip); txn.commit(); } } } } @Override public boolean releasePublicIpAddress(Ip addr, long ownerId, long userId) { IPAddressVO ip = _ipAddressDao.markAsUnavailable(addr, ownerId); assert (ip != null) : "Unable to mark the ip address " + addr + " owned by " + ownerId + " as unavailable."; if (ip == null) { return true; } if (s_logger.isDebugEnabled()) { s_logger.debug("Releasing ip " + addr + "; sourceNat = " + ip.isSourceNat()); } boolean success = true; try { if (!_rulesMgr.revokeAllRules(addr, userId)) { s_logger.warn("Unable to revoke all the port forwarding rules for ip " + ip); success = false; } } catch (ResourceUnavailableException e) { s_logger.warn("Unable to revoke all the port forwarding rules for ip " + ip, e); success = false; } if (!_lbMgr.removeAllLoadBalanacers(addr)) { s_logger.warn("Unable to revoke all the load balancer rules for ip " + ip); success = false; } if (ip.getAssociatedWithNetworkId() != null) { Network network = _networksDao.findById(ip.getAssociatedWithNetworkId()); try { if (!applyIpAssociations(network, true)) { s_logger.warn("Unable to apply ip address associations for " + network); success = false; } } catch (ResourceUnavailableException e) { throw new CloudRuntimeException("We should nver get to here because we used true when applyIpAssociations", e); } } if (success) { _ipAddressDao.unassignIpAddress(addr); s_logger.debug("released a public ip: " + addr); if(ownerId != Account.ACCOUNT_ID_SYSTEM){ UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_NET_IP_RELEASE, ownerId, ip.getDataCenterId(), 0, addr.toString()); _usageEventDao.persist(usageEvent); } _accountMgr.decrementResourceCount(ownerId, ResourceType.public_ip); } return success; } @Override public boolean configure(final String name, final Map params) throws ConfigurationException { _name = name; _configs = _configDao.getConfiguration("AgentManager", params); _networkGcWait = NumbersUtil.parseInt(_configs.get(Config.NetworkGcWait.key()), 600); _networkGcInterval = NumbersUtil.parseInt(_configs.get(Config.NetworkGcInterval.key()), 600); _configs = _configDao.getConfiguration("Network", params); _networkDomain = _configs.get(Config.GuestDomainSuffix.key()); NetworkOfferingVO publicNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemPublicNetwork, TrafficType.Public); publicNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(publicNetworkOffering); _systemNetworks.put(NetworkOfferingVO.SystemPublicNetwork, publicNetworkOffering); NetworkOfferingVO managementNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemManagementNetwork, TrafficType.Management); managementNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(managementNetworkOffering); _systemNetworks.put(NetworkOfferingVO.SystemManagementNetwork, managementNetworkOffering); NetworkOfferingVO controlNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemControlNetwork, TrafficType.Control); controlNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(controlNetworkOffering); _systemNetworks.put(NetworkOfferingVO.SystemControlNetwork, controlNetworkOffering); NetworkOfferingVO storageNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemStorageNetwork, TrafficType.Storage); storageNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(storageNetworkOffering); _systemNetworks.put(NetworkOfferingVO.SystemStorageNetwork, storageNetworkOffering); NetworkOfferingVO guestNetworkOffering = new NetworkOfferingVO(NetworkOfferingVO.SystemGuestNetwork, TrafficType.Guest); guestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(guestNetworkOffering); _systemNetworks.put(NetworkOfferingVO.SystemGuestNetwork, guestNetworkOffering); NetworkOfferingVO defaultGuestNetworkOffering = new NetworkOfferingVO(NetworkOffering.DefaultVirtualizedNetworkOffering, "Virtual Vlan", TrafficType.Guest, false, false, null, null, null, true, Availability.Required, false, false, false, false, false, false, false); defaultGuestNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestNetworkOffering); NetworkOfferingVO defaultGuestDirectNetworkOffering = new NetworkOfferingVO(NetworkOffering.DefaultDirectNetworkOffering, "Direct", TrafficType.Public, false, false, null, null, null, true, Availability.Required, false, false, false, false, false, false, false); defaultGuestDirectNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultGuestDirectNetworkOffering); AccountsUsingNetworkSearch = _accountDao.createSearchBuilder(); SearchBuilder networkAccountSearch = _networksDao.createSearchBuilderForAccount(); AccountsUsingNetworkSearch.join("nc", networkAccountSearch, AccountsUsingNetworkSearch.entity().getId(), networkAccountSearch.entity().getAccountId(), JoinType.INNER); networkAccountSearch.and("config", networkAccountSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); networkAccountSearch.and("owner", networkAccountSearch.entity().isOwner(), SearchCriteria.Op.EQ); AccountsUsingNetworkSearch.done(); AssignIpAddressSearch = _ipAddressDao.createSearchBuilder(); AssignIpAddressSearch.and("dc", AssignIpAddressSearch.entity().getDataCenterId(), Op.EQ); AssignIpAddressSearch.and("allocated", AssignIpAddressSearch.entity().getAllocatedTime(), Op.NULL); AssignIpAddressSearch.and("vlanId", AssignIpAddressSearch.entity().getVlanId(), Op.EQ); SearchBuilder vlanSearch = _vlanDao.createSearchBuilder(); vlanSearch.and("type", vlanSearch.entity().getVlanType(), Op.EQ); vlanSearch.and("networkId", vlanSearch.entity().getNetworkId(), Op.EQ); AssignIpAddressSearch.join("vlan", vlanSearch, vlanSearch.entity().getId(), AssignIpAddressSearch.entity().getVlanId(), JoinType.INNER); AssignIpAddressSearch.done(); AssignIpAddressFromPodVlanSearch = _ipAddressDao.createSearchBuilder(); AssignIpAddressFromPodVlanSearch.and("dc", AssignIpAddressFromPodVlanSearch.entity().getDataCenterId(), Op.EQ); AssignIpAddressFromPodVlanSearch.and("allocated", AssignIpAddressFromPodVlanSearch.entity().getAllocatedTime(), Op.NULL); SearchBuilder podVlanSearch = _vlanDao.createSearchBuilder(); podVlanSearch.and("type", podVlanSearch.entity().getVlanType(), Op.EQ); podVlanSearch.and("networkId", podVlanSearch.entity().getNetworkId(), Op.EQ); SearchBuilder podVlanMapSB = _podVlanMapDao.createSearchBuilder(); podVlanMapSB.and("podId", podVlanMapSB.entity().getPodId(), Op.EQ); AssignIpAddressFromPodVlanSearch.join("podVlanMapSB", podVlanMapSB, podVlanMapSB.entity().getVlanDbId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER); AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER); AssignIpAddressFromPodVlanSearch.done(); IpAddressSearch = _ipAddressDao.createSearchBuilder(); IpAddressSearch.and("accountId", IpAddressSearch.entity().getAllocatedToAccountId(), Op.EQ); IpAddressSearch.and("dataCenterId", IpAddressSearch.entity().getDataCenterId(), Op.EQ); SearchBuilder virtualNetworkVlanSB = _vlanDao.createSearchBuilder(); virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), Op.EQ); IpAddressSearch.join("virtualNetworkVlanSB", virtualNetworkVlanSB, IpAddressSearch.entity().getVlanId(), virtualNetworkVlanSB.entity().getId(), JoinBuilder.JoinType.INNER); IpAddressSearch.done(); NicForTrafficTypeSearch = _nicDao.createSearchBuilder(); SearchBuilder networkSearch = _networksDao.createSearchBuilder(); NicForTrafficTypeSearch.join("network", networkSearch, networkSearch.entity().getId(), NicForTrafficTypeSearch.entity().getNetworkId(), JoinType.INNER); NicForTrafficTypeSearch.and("instance", NicForTrafficTypeSearch.entity().getInstanceId(), Op.EQ); networkSearch.and("traffictype", networkSearch.entity().getTrafficType(), Op.EQ); NicForTrafficTypeSearch.done(); _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Network-Scavenger")); s_logger.info("Network Manager is configured."); return true; } @Override public String getName() { return _name; } @Override public boolean start() { _executor.scheduleWithFixedDelay(new NetworkGarbageCollector(), _networkGcInterval, _networkGcInterval, TimeUnit.SECONDS); return true; } @Override public boolean stop() { return true; } protected NetworkManagerImpl() { } @Override public List listPublicIpAddressesInVirtualNetwork(long accountId, long dcId, Boolean sourceNat) { SearchCriteria sc = IpAddressSearch.create(); sc.setParameters("accountId", accountId); sc.setParameters("dataCenterId", dcId); if (sourceNat != null) { sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat); } sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork); return _ipAddressDao.search(sc, null); } @Override public List setupNetwork(Account owner, NetworkOfferingVO offering, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException { return setupNetwork(owner, offering, null, plan, name, displayText, isShared, isDefault); } @Override @DB public List setupNetwork(Account owner, NetworkOfferingVO offering, Network predefined, DeploymentPlan plan, String name, String displayText, boolean isShared, boolean isDefault) throws ConcurrentOperationException { Transaction.currentTxn(); Account locked = _accountDao.acquireInLockTable(owner.getId()); if (locked == null) { throw new ConcurrentOperationException("Unable to acquire lock on " + owner); } try { if (predefined == null || (predefined.getBroadcastUri() == null && predefined.getBroadcastDomainType() != BroadcastDomainType.Vlan)) { List configs = _networksDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId()); if (configs.size() > 0) { if (s_logger.isDebugEnabled()) { s_logger.debug("Found existing network configuration for offering " + offering + ": " + configs.get(0)); } return configs; } } List configs = new ArrayList(); long related = -1; for (NetworkGuru guru : _networkGurus) { Network config = guru.design(offering, plan, predefined, owner); if (config == null) { continue; } if (config.getId() != -1) { if (config instanceof NetworkVO) { configs.add((NetworkVO) config); } else { configs.add(_networksDao.findById(config.getId())); } continue; } long id = _networksDao.getNextInSequence(Long.class, "id"); if (related == -1) { related = id; } NetworkVO vo = new NetworkVO(id, config, offering.getId(), plan.getDataCenterId(), guru.getName(), owner.getDomainId(), owner.getId(), related, name, displayText, isShared, isDefault); configs.add(_networksDao.persist(vo, vo.getGuestType() != null)); } if (configs.size() < 1) { throw new CloudRuntimeException("Unable to convert network offering to network profile: " + offering.getId()); } return configs; } finally { s_logger.debug("Releasing lock for " + locked); _accountDao.releaseFromLockTable(locked.getId()); } } @Override public List getSystemAccountNetworkOfferings(String... offeringNames) { List offerings = new ArrayList(offeringNames.length); for (String offeringName : offeringNames) { NetworkOfferingVO network = _systemNetworks.get(offeringName); if (network == null) { throw new CloudRuntimeException("Unable to find system network profile for " + offeringName); } offerings.add(network); } return offerings; } @Override @DB public void allocate(VirtualMachineProfile vm, List> networks) throws InsufficientCapacityException, ConcurrentOperationException { Transaction txn = Transaction.currentTxn(); txn.start(); int deviceId = 0; boolean[] deviceIds = new boolean[networks.size()]; Arrays.fill(deviceIds, false); List nics = new ArrayList(networks.size()); NicVO defaultNic = null; for (Pair network : networks) { NetworkVO config = network.first(); NetworkGuru concierge = _networkGurus.get(config.getGuruName()); NicProfile requested = network.second(); if (requested != null && requested.getMode() == null) { requested.setMode(config.getMode()); } NicProfile profile = concierge.allocate(config, requested, vm); if (vm != null && vm.getVirtualMachine().getType() == Type.User && config.isDefault()) { profile.setDefaultNic(true); } if (profile == null) { continue; } NicVO vo = new NicVO(concierge.getName(), vm.getId(), config.getId()); while (deviceIds[deviceId] && deviceId < deviceIds.length) { deviceId++; } deviceId = applyProfileToNic(vo, profile, deviceId); vo = _nicDao.persist(vo); if (vo.isDefaultNic()) { if (defaultNic != null) { throw new IllegalArgumentException("You cannot specify two nics as default nics: nic 1 = " + defaultNic + "; nic 2 = " + vo); } defaultNic = vo; } int devId = vo.getDeviceId(); if (devId > deviceIds.length) { throw new IllegalArgumentException("Device id for nic is too large: " + vo); } if (deviceIds[devId]) { throw new IllegalArgumentException("Conflicting device id for two different nics: " + devId); } deviceIds[devId] = true; nics.add(vo); NetworkOffering no = _configMgr.getNetworkOffering(config.getNetworkOfferingId()); Integer networkRate = _configMgr.getNetworkRate(no.getId()); vm.addNic(new NicProfile(vo, network.first(), vo.getBroadcastUri(), vo.getIsolationUri(), networkRate)); } if (nics.size() == 1) { nics.get(0).setDefaultNic(true); } txn.commit(); } protected Integer applyProfileToNic(NicVO vo, NicProfile profile, Integer deviceId) { if (profile.getDeviceId() != null) { vo.setDeviceId(profile.getDeviceId()); } else if (deviceId != null) { vo.setDeviceId(deviceId++); } vo.setReservationStrategy(profile.getReservationStrategy()); vo.setDefaultNic(profile.isDefaultNic()); if (profile.getIp4Address() != null) { vo.setIp4Address(profile.getIp4Address()); vo.setAddressFormat(AddressFormat.Ip4); } if (profile.getMacAddress() != null) { vo.setMacAddress(profile.getMacAddress()); } vo.setMode(profile.getMode()); vo.setNetmask(profile.getNetmask()); vo.setGateway(profile.getGateway()); if (profile.getBroadCastUri() != null) { vo.setBroadcastUri(profile.getBroadCastUri()); } if (profile.getIsolationUri() != null) { vo.setIsolationUri(profile.getIsolationUri()); } vo.setState(Nic.State.Allocated); return deviceId; } protected NicTO toNicTO(NicVO nic, NicProfile profile, NetworkVO config) { NicTO to = new NicTO(); to.setDeviceId(nic.getDeviceId()); to.setBroadcastType(config.getBroadcastDomainType()); to.setType(config.getTrafficType()); to.setIp(nic.getIp4Address()); to.setNetmask(nic.getNetmask()); to.setMac(nic.getMacAddress()); to.setDns1(profile.getDns1()); to.setDns2(profile.getDns2()); if (nic.getGateway() != null) { to.setGateway(nic.getGateway()); } else { to.setGateway(config.getGateway()); } to.setDefaultNic(nic.isDefaultNic()); to.setBroadcastUri(nic.getBroadcastUri()); to.setIsolationuri(nic.getIsolationUri()); if (profile != null) { to.setDns1(profile.getDns1()); to.setDns2(profile.getDns2()); } Integer networkRate = _configMgr.getNetworkRate(config.getNetworkOfferingId()); to.setNetworkRateMbps(networkRate); return to; } @Override @DB public Pair implementNetwork(long networkId, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { Transaction.currentTxn(); Pair implemented = new Pair(null, null); NetworkVO network = _networksDao.acquireInLockTable(networkId); if (network == null) { throw new ConcurrentOperationException("Unable to acquire network configuration: " + networkId); } try { NetworkGuru guru = _networkGurus.get(network.getGuruName()); Network.State state = network.getState(); if (state == Network.State.Implemented || state == Network.State.Setup || state == Network.State.Implementing) { implemented.set(guru, network); return implemented; } if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + guru + " to implement " + network); } NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); network.setReservationId(context.getReservationId()); network.setState(Network.State.Implementing); _networksDao.update(networkId, network); Network result = guru.implement(network, offering, dest, context); network.setCidr(result.getCidr()); network.setBroadcastUri(result.getBroadcastUri()); network.setGateway(result.getGateway()); network.setMode(result.getMode()); _networksDao.update(networkId, network); for (NetworkElement element : _networkElements) { if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to implmenet " + network); } element.implement(network, offering, dest, context); } network.setState(Network.State.Implemented); _networksDao.update(network.getId(), network); implemented.set(guru, network); return implemented; } finally { if (implemented.first() == null) { s_logger.debug("Cleaning up because we're unable to implement network " + network); network.setState(Network.State.Shutdown); _networksDao.update(networkId, network); shutdownNetwork(networkId); } _networksDao.releaseFromLockTable(networkId); } } @DB protected void updateNic(NicVO nic, long networkId, int count) { Transaction txn = Transaction.currentTxn(); txn.start(); _nicDao.update(nic.getId(), nic); _networksDao.changeActiveNicsBy(networkId, count); txn.commit(); } @Override public void prepare(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { List nics = _nicDao.listBy(vmProfile.getId()); for (NicVO nic : nics) { Pair implemented = implementNetwork(nic.getNetworkId(), dest, context); NetworkGuru concierge = implemented.first(); NetworkVO network = implemented.second(); NetworkOffering no = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); Integer networkRate = _configMgr.getNetworkRate(no.getId()); NicProfile profile = null; if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) { nic.setState(Nic.State.Reserving); nic.setReservationId(context.getReservationId()); _nicDao.update(nic.getId(), nic); URI broadcastUri = nic.getBroadcastUri(); if (broadcastUri == null) { broadcastUri = network.getBroadcastUri(); } URI isolationUri = nic.getIsolationUri(); profile = new NicProfile(nic, network, broadcastUri, isolationUri, networkRate); concierge.reserve(profile, network, vmProfile, dest, context); nic.setIp4Address(profile.getIp4Address()); nic.setIp6Address(profile.getIp6Address()); nic.setMacAddress(profile.getMacAddress()); nic.setIsolationUri(profile.getIsolationUri()); nic.setBroadcastUri(profile.getBroadCastUri()); nic.setReserver(concierge.getName()); nic.setState(Nic.State.Reserved); nic.setNetmask(profile.getNetmask()); nic.setGateway(profile.getGateway()); nic.setAddressFormat(profile.getFormat()); updateNic(nic, network.getId(), 1); } else { profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate); nic.setState(Nic.State.Reserved); updateNic(nic, network.getId(), 1); } for (NetworkElement element : _networkElements) { if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to prepare for " + nic); } element.prepare(network, profile, vmProfile, dest, context); } concierge.updateNicProfile(profile, network); vmProfile.addNic(profile); } } @Override public void prepareNicForMigration(VirtualMachineProfile vm, DeployDestination dest) { List nics = _nicDao.listBy(vm.getId()); for (NicVO nic : nics) { NetworkVO network = _networksDao.findById(nic.getNetworkId()); NetworkOffering no = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); Integer networkRate = _configMgr.getNetworkRate(no.getId()); NetworkGuru concierge = _networkGurus.get(network.getGuruName()); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate); concierge.updateNicProfile(profile, network); vm.addNic(profile); } } @Override public void release(VirtualMachineProfile vmProfile, boolean forced) { List nics = _nicDao.listBy(vmProfile.getId()); for (NicVO nic : nics) { NetworkVO network = _networksDao.findById(nic.getNetworkId()); if (nic.getState() == Nic.State.Reserved || nic.getState() == Nic.State.Reserving) { Nic.State originalState = nic.getState(); if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start) { NetworkGuru concierge = _networkGurus.get(network.getGuruName()); nic.setState(Nic.State.Releasing); _nicDao.update(nic.getId(), nic); NicProfile profile = new NicProfile(nic, network, null, null, null); if (concierge.release(profile, vmProfile, nic.getReservationId())) { nic.setState(Nic.State.Allocated); if (originalState == Nic.State.Reserved) { updateNic(nic, network.getId(), -1); } else { _nicDao.update(nic.getId(), nic); } } } else { nic.setState(Nic.State.Allocated); updateNic(nic, network.getId(), -1); } } } } @Override public List getNics(VirtualMachine vm) { return _nicDao.listBy(vm.getId()); } private Account findAccountByIpAddress(Ip ipAddress) { IPAddressVO address = _ipAddressDao.findById(ipAddress); if ((address != null) && (address.getAllocatedToAccountId() != null)) { return _accountMgr.getActiveAccount(address.getAllocatedToAccountId()); } return null; } @Override public List getNicProfiles(VirtualMachine vm) { List nics = _nicDao.listBy(vm.getId()); List profiles = new ArrayList(); if (nics != null) { for (Nic nic : nics) { NetworkVO network = _networksDao.findById(nic.getNetworkId()); NetworkOffering no = _configMgr.getNetworkOffering(network.getNetworkOfferingId()); Integer networkRate = _configMgr.getNetworkRate(no.getId()); NetworkGuru concierge = _networkGurus.get(network.getGuruName()); NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate); concierge.updateNicProfile(profile, network); profiles.add(profile); } } return profiles; } @Override @DB public boolean disassociateIpAddress(DisassociateIPAddrCmd cmd) throws PermissionDeniedException, IllegalArgumentException { Long userId = UserContext.current().getCallerUserId(); Account caller = UserContext.current().getCaller(); Ip ipAddress = cmd.getIpAddress(); // Verify input parameters Account accountByIp = findAccountByIpAddress(ipAddress); if (accountByIp == null) { throw new InvalidParameterValueException("Unable to find account owner for ip " + ipAddress); } Long accountId = accountByIp.getId(); if (!_accountMgr.isAdmin(caller.getType())) { if (caller.getId() != accountId.longValue()) { throw new PermissionDeniedException("account " + caller.getAccountName() + " doesn't own ip address " + ipAddress); } } else { Domain domain = _domainDao.findById(accountByIp.getDomainId()); _accountMgr.checkAccess(caller, domain); } try { IPAddressVO ipVO = _ipAddressDao.findById(ipAddress); if (ipVO == null) { return false; } if (ipVO.getAllocatedTime() == null) { return true; } Account account = _accountMgr.getAccount(accountId); if (account == null) { return false; } if ((ipVO.getAllocatedToAccountId() == null) || (ipVO.getAllocatedToAccountId().longValue() != accountId)) { // FIXME: is the user visible in the admin account's domain???? if (!BaseCmd.isAdmin(account.getType())) { if (s_logger.isDebugEnabled()) { s_logger.debug("permission denied disassociating IP address " + ipAddress + "; acct: " + accountId + "; ip (acct / dc / dom / alloc): " + ipVO.getAllocatedToAccountId() + " / " + ipVO.getDataCenterId() + " / " + ipVO.getAllocatedInDomainId() + " / " + ipVO.getAllocatedTime()); } throw new PermissionDeniedException("User/account does not own supplied address"); } } if (ipVO.getAllocatedTime() == null) { return true; } if (ipVO.isSourceNat()) { throw new IllegalArgumentException("ip address is used for source nat purposes and can not be disassociated."); } VlanVO vlan = _vlanDao.findById(ipVO.getVlanId()); if (!vlan.getVlanType().equals(VlanType.VirtualNetwork)) { throw new IllegalArgumentException("only ip addresses that belong to a virtual network may be disassociated."); } // Check for account wide pool. It will have an entry for account_vlan_map. if (_accountVlanMapDao.findAccountVlanMap(accountId, ipVO.getVlanId()) != null) { throw new PermissionDeniedException(ipAddress + " belongs to Account wide IP pool and cannot be disassociated"); } return releasePublicIpAddress(ipAddress, accountId, userId); } catch (PermissionDeniedException pde) { throw pde; } catch (IllegalArgumentException iae) { throw iae; } catch (Throwable t) { s_logger.error("Disassociate IP address threw an exception.", t); throw new IllegalArgumentException("Disassociate IP address threw an exception"); } } @Override public List getAccountsUsingNetwork(long networkId) { SearchCriteria sc = AccountsUsingNetworkSearch.create(); sc.setJoinParameters("nc", "config", networkId); return _accountDao.search(sc, null); } @Override public AccountVO getNetworkOwner(long networkId) { SearchCriteria sc = AccountsUsingNetworkSearch.create(); sc.setJoinParameters("nc", "config", networkId); sc.setJoinParameters("nc", "owner", true); List accounts = _accountDao.search(sc, null); return accounts.size() != 0 ? accounts.get(0) : null; } @Override public List getNetworksforOffering(long offeringId, long dataCenterId, long accountId) { return _networksDao.getNetworksForOffering(offeringId, dataCenterId, accountId); } @Override public List listNetworkOfferings() { return _networkOfferingDao.listNonSystemNetworkOfferings(); } @Override public String getNextAvailableMacAddressInNetwork(long networkId) throws InsufficientAddressCapacityException { String mac = _networksDao.getNextAvailableMacAddress(networkId); if (mac == null) { throw new InsufficientAddressCapacityException("Unable to create another mac address", Network.class, networkId); } return mac; } @Override @DB public Network getNetwork(long id) { return _networksDao.findById(id); } @Override public List getRemoteAccessVpnElements() { List elements = new ArrayList(); for (NetworkElement element : _networkElements) { if (element instanceof RemoteAccessVpnElement) { elements.add((RemoteAccessVpnElement) element); } } return elements; } @Override public void cleanupNics(VirtualMachineProfile vm) { List nics = _nicDao.listBy(vm.getId()); for (NicVO nic : nics) { nic.setState(Nic.State.Deallocating); _nicDao.update(nic.getId(), nic); NetworkVO network = _networksDao.findById(nic.getNetworkId()); NicProfile profile = new NicProfile(nic, network, null, null, null); NetworkGuru guru = _networkGurus.get(network.getGuruName()); guru.deallocate(network, profile, vm); _nicDao.remove(nic.getId()); } } @Override public void expungeNics(VirtualMachineProfile vm) { List nics = _nicDao.listIncludingRemovedBy(vm.getId()); for (NicVO nic : nics) { _nicDao.expunge(nic.getId()); } } @Override public Network createNetwork(CreateNetworkCmd cmd) throws InvalidParameterValueException, PermissionDeniedException { Long networkOfferingId = cmd.getNetworkOfferingId(); Long zoneId = cmd.getZoneId(); String gateway = cmd.getGateway(); String startIP = cmd.getStartIp(); String endIP = cmd.getEndIp(); String netmask = cmd.getNetmask(); String networkDomain = cmd.getNetworkDomain(); String vlanId = cmd.getVlan(); String name = cmd.getNetworkName(); String displayText = cmd.getDisplayText(); Boolean isShared = cmd.getIsShared(); Boolean isDefault = cmd.isDefault(); //finalize owner for the network Account ctxAccount = UserContext.current().getCaller(); String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); Account owner = _accountMgr.finalizeOwner(ctxAccount, accountName, domainId); return createNetwork(networkOfferingId, name, displayText, isShared, isDefault, zoneId, gateway, startIP, endIP, netmask, vlanId, networkDomain, owner); } @Override @DB public Network createNetwork(long networkOfferingId, String name, String displayText, Boolean isShared, Boolean isDefault, Long zoneId, String gateway, String startIP, String endIP, String netmask, String vlanId, String networkDomain, Account owner) throws InvalidParameterValueException, PermissionDeniedException { Account ctxAccount = UserContext.current().getCaller(); Long userId = UserContext.current().getCallerUserId(); String cidr = null; if (gateway != null && netmask != null) { cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); } // if end ip is not specified, default it to startIp if (endIP == null && startIP != null) { endIP = startIP; } // Check if network offering exists NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); if (networkOffering == null || networkOffering.isSystemOnly()) { throw new InvalidParameterValueException("Unable to find network offeirng by id " + networkOfferingId); } // allow isDefault to be set only for Virtual network if (networkOffering.getTrafficType() == TrafficType.Guest) { if (isDefault != null) { throw new InvalidParameterValueException("Can specify isDefault parameter only for Public network. "); } else { isDefault = true; } } else { if (isDefault == null) { isDefault = false; } } // If networkDomain is not specified, take it from the global configuration if (networkDomain == null) { networkDomain = _networkDomain; } // Check if zone exists if (zoneId == null || ((_dcDao.findById(zoneId)) == null)) { throw new InvalidParameterValueException("Please specify a valid zone."); } DataCenter zone = _dcDao.findById(zoneId); if (zone.getNetworkType() == NetworkType.Basic) { throw new InvalidParameterValueException("Network creation is not allowed in zone with network type " + NetworkType.Basic); } // Don't allow to create network with vlan that already exists in the system if (vlanId != null) { String uri = "vlan://" + vlanId; List networks = _networksDao.listBy(zoneId, uri); if ((networks != null && !networks.isEmpty())) { throw new InvalidParameterValueException("Network with vlan " + vlanId + " already exists in zone " + zoneId); } } // VlanId can be specified only when network offering supports it if (ctxAccount.getType() == Account.ACCOUNT_TYPE_NORMAL && vlanId != null && !networkOffering.getSpecifyVlan()) { throw new InvalidParameterValueException("Can't specify vlan because network offering doesn't support it"); } Transaction txn = Transaction.currentTxn(); txn.start(); try { // Create network DataCenterDeployment plan = new DataCenterDeployment(zoneId, null, null, null); NetworkVO userNetwork = new NetworkVO(); userNetwork.setNetworkDomain(networkDomain); // cidr should be set only when the user is admin if (ctxAccount.getType() == Account.ACCOUNT_TYPE_ADMIN) { if (cidr != null && gateway != null) { userNetwork.setCidr(cidr); userNetwork.setGateway(gateway); if (vlanId != null) { userNetwork.setBroadcastUri(URI.create("vlan://" + vlanId)); userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan); if (!vlanId.equalsIgnoreCase(Vlan.UNTAGGED)) { userNetwork.setBroadcastDomainType(BroadcastDomainType.Vlan); } else { userNetwork.setBroadcastDomainType(BroadcastDomainType.Native); } } } } List networks = setupNetwork(owner, networkOffering, userNetwork, plan, name, displayText, isShared, isDefault); Long networkId = null; Network network = null; if (networks == null || networks.isEmpty()) { txn.rollback(); throw new CloudRuntimeException("Fail to create a network"); } else { network = networks.get(0); networkId = networks.get(0).getId(); if (network.getGuestType() == GuestIpType.Virtual) { s_logger.debug("Creating a source natp ip for " + network); PublicIp ip = assignSourceNatIpAddress(owner, network, userId); if (ip == null) { throw new InsufficientAddressCapacityException("Unable to assign source nat ip address to owner for this network", DataCenter.class, zoneId); } } } // Don't pass owner to create vlan when network offering is of type Direct - done to prevent accountVlanMap entry // creation when vlan is mapped to network if (network.getGuestType() == GuestIpType.Direct) { owner = null; } if (ctxAccount.getType() == Account.ACCOUNT_TYPE_ADMIN && network.getGuestType() == GuestIpType.Direct && startIP != null && endIP != null && gateway != null) { // Create vlan ip range Vlan vlan = _configMgr.createVlanAndPublicIpRange(userId, zoneId, null, startIP, endIP, gateway, netmask, false, vlanId, owner, networkId); if (vlan == null) { txn.rollback(); throw new CloudRuntimeException("Failed to create a vlan"); } } txn.commit(); return networks.get(0); } catch (Exception ex) { s_logger.warn("Unexpected exception while creating network ", ex); txn.rollback(); } finally { txn.close(); } return null; } @Override public List searchForNetworks(ListNetworksCmd cmd) { Object id = cmd.getId(); Object keyword = cmd.getKeyword(); Long zoneId = cmd.getZoneId(); Account account = UserContext.current().getCaller(); Long domainId = cmd.getDomainId(); String accountName = cmd.getAccountName(); String type = cmd.getType(); String trafficType = cmd.getTrafficType(); Boolean isSystem = cmd.getIsSystem(); Boolean isShared = cmd.getIsShared(); Boolean isDefault = cmd.isDefault(); Long accountId = null; if (isSystem == null) { isSystem = false; } // Account/domainId parameters and isSystem are mutually exclusive if (isSystem && (accountName != null || domainId != null)) { throw new InvalidParameterValueException("System network belongs to system, account and domainId parameters can't be specified"); } if (_accountMgr.isAdmin(account.getType())) { if (domainId != null) { if ((account != null) && !_domainDao.isChildDomain(account.getDomainId(), domainId)) { throw new PermissionDeniedException("Invalid domain id (" + domainId + ") given, unable to list networks"); } if (accountName != null) { account = _accountMgr.getActiveAccount(accountName, domainId); if (account == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); } accountId = account.getId(); } } else { accountId = account.getId(); } } else { accountName = account.getAccountName(); domainId = account.getDomainId(); accountId = account.getId(); } Filter searchFilter = new Filter(NetworkVO.class, "id", false, cmd.getStartIndex(), cmd.getPageSizeVal()); SearchBuilder sb = _networksDao.createSearchBuilder(); // Don't display networks created of system network offerings SearchBuilder networkOfferingSearch = _networkOfferingDao.createSearchBuilder(); networkOfferingSearch.and("systemOnly", networkOfferingSearch.entity().isSystemOnly(), SearchCriteria.Op.EQ); if (isSystem) { networkOfferingSearch.and("trafficType", networkOfferingSearch.entity().getTrafficType(), SearchCriteria.Op.EQ); } sb.join("networkOfferingSearch", networkOfferingSearch, sb.entity().getNetworkOfferingId(), networkOfferingSearch.entity().getId(), JoinBuilder.JoinType.INNER); SearchBuilder zoneSearch = _dcDao.createSearchBuilder(); zoneSearch.and("networkType", zoneSearch.entity().getNetworkType(), SearchCriteria.Op.EQ); sb.join("zoneSearch", zoneSearch, sb.entity().getDataCenterId(), zoneSearch.entity().getId(), JoinBuilder.JoinType.INNER); SearchCriteria sc = sb.create(); if (!isSystem) { sc.setJoinParameters("networkOfferingSearch", "systemOnly", false); } else { sc.setJoinParameters("networkOfferingSearch", "systemOnly", true); sc.setJoinParameters("zoneSearch", "networkType", NetworkType.Advanced.toString()); } if (keyword != null) { SearchCriteria ssc = _networksDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } if (id != null) { sc.addAnd("id", SearchCriteria.Op.EQ, id); } if (zoneId != null) { sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId); } if (type != null) { sc.addAnd("guestType", SearchCriteria.Op.EQ, type); } if (!isSystem && (isShared == null || !isShared) && accountName != null && domainId != null) { sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); sc.addAnd("isShared", SearchCriteria.Op.EQ, false); } if (isShared != null) { sc.addAnd("isShared", SearchCriteria.Op.EQ, isShared); } if (isDefault != null) { sc.addAnd("isDefault", SearchCriteria.Op.EQ, isDefault); } if (trafficType != null) { sc.addAnd("trafficType", SearchCriteria.Op.EQ, trafficType); } List networks = _networksDao.search(sc, searchFilter); return networks; } @Override @DB public boolean deleteNetwork(long networkId) throws InvalidParameterValueException, PermissionDeniedException { Long userId = UserContext.current().getCallerUserId(); Account caller = UserContext.current().getCaller(); // Verify network id NetworkVO network = _networksDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("unable to find network " + networkId); } // Perform permission check if (!_accountMgr.isAdmin(caller.getType())) { if (network.getAccountId() != caller.getId()) { throw new PermissionDeniedException("Account " + caller.getAccountName() + " does not own network id=" + networkId + ", permission denied"); } } else { Account owner = _accountMgr.getAccount(network.getAccountId()); _accountMgr.checkAccess(caller, owner); } return this.destroyNetwork(networkId, userId); } @Override @DB public void shutdownNetwork(long networkId) { Transaction txn = Transaction.currentTxn(); txn.start(); NetworkVO network = _networksDao.lockRow(networkId, true); if (network == null) { s_logger.debug("Unable to find network with id: " + networkId); return; } if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Shutdown) { s_logger.debug("Network is not implemented: " + network); return; } network.setState(Network.State.Shutdown); _networksDao.update(network.getId(), network); txn.commit(); boolean success = true; for (NetworkElement element : _networkElements) { try { if (s_logger.isDebugEnabled()) { s_logger.debug("Sending network shutdown to " + element); } element.shutdown(network, null); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e); success = false; } catch (ConcurrentOperationException e) { s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e); success = false; } catch (Exception e) { s_logger.warn("Unable to complete shutdown of the network due to element: " + element.getName(), e); success = false; } } txn.start(); if (success) { if (s_logger.isDebugEnabled()) { s_logger.debug("Network id=" + networkId + " is shutdown successfully, cleaning up corresponding resources now."); } NetworkGuru guru = _networkGurus.get(network.getGuruName()); guru.destroy(network, _networkOfferingDao.findById(network.getNetworkOfferingId())); network.setBroadcastUri(null); network.setState(Network.State.Allocated); _networksDao.update(network.getId(), network); _networksDao.clearCheckForGc(networkId); } else { network.setState(Network.State.Implemented); _networksDao.update(network.getId(), network); } txn.commit(); } @DB @Override public boolean destroyNetwork(long networkId, long callerUserId) { NetworkVO network = _networksDao.findById(networkId); if (network == null) { s_logger.debug("Unable to find network with id: " + networkId); return false; } // Shutdown network first shutdownNetwork(networkId); // get updated state for the network network = _networksDao.findById(networkId); if (network.getState() != Network.State.Allocated && network.getState() != Network.State.Setup) { s_logger.debug("Network is not not in the correct state to be destroyed: " + network.getState()); return false; } boolean success = true; //release ip addresses associated with the network if there are any List ipsToRelease = _ipAddressDao.listByAssociatedNetwork(networkId); if (ipsToRelease != null && !ipsToRelease.isEmpty()) { for (IPAddressVO ip : ipsToRelease) { _ipAddressDao.unassignIpAddress(ip.getAddress()); } s_logger.debug("Ip addresses are unassigned successfully as a part of network id=" + networkId + " destroy"); } for (NetworkElement element : _networkElements) { try { if (s_logger.isDebugEnabled()) { s_logger.debug("Sending destroy to " + element); } element.destroy(network); } catch (ResourceUnavailableException e) { s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e); success = false; } catch (ConcurrentOperationException e) { s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e); success = false; } catch (Exception e) { s_logger.warn("Unable to complete destroy of the network due to element: " + element.getName(), e); success = false; } } if (success) { if (s_logger.isDebugEnabled()) { s_logger.debug("Network id=" + networkId + " is destroyed successfully, cleaning up corresponding resources now."); } NetworkGuru guru = _networkGurus.get(network.getGuruName()); Account owner = _accountMgr.getAccount(network.getAccountId()); Transaction txn = Transaction.currentTxn(); txn.start(); guru.trash(network, _networkOfferingDao.findById(network.getNetworkOfferingId()), owner); if (!deleteVlansInNetwork(network.getId(), callerUserId)) { success = false; s_logger.warn("Failed to delete network " + network + "; was unable to cleanup corresponding ip ranges"); } else { // commit transaction only when ips and vlans for the network are released successfully network.setState(Network.State.Destroy); _networksDao.update(network.getId(), network); _networksDao.remove(network.getId()); txn.commit(); } } return success; } private boolean deleteVlansInNetwork(long networkId, long userId) { List vlans = _vlanDao.listVlansByNetworkId(networkId); boolean result = true; for (VlanVO vlan : vlans) { if (!_configMgr.deleteVlanAndPublicIpRange(_accountMgr.getSystemUser().getId(), vlan.getId())) { s_logger.warn("Failed to delete vlan " + vlan.getId() + ");"); result = false; } } return result; } @Override public boolean applyRules(List rules, boolean continueOnError) throws ResourceUnavailableException { if (rules == null || rules.size() == 0) { s_logger.debug("There are no rules to forward to the network elements"); return true; } boolean success = true; Network network = _networksDao.findById(rules.get(0).getNetworkId()); for (NetworkElement ne : _networkElements) { try { boolean handled = ne.applyRules(network, rules); s_logger.debug("Network Rules for network " + network.getId() + " were " + (handled ? "" : " not") + " handled by " + ne.getName()); } catch (ResourceUnavailableException e) { if (!continueOnError) { throw e; } s_logger.warn("Problems with " + ne.getName() + " but pushing on", e); success = false; } } return success; } public class NetworkGarbageCollector implements Runnable { @Override public void run() { try { List shutdownList = new ArrayList(); long currentTime = System.currentTimeMillis() >> 10; HashMap stillFree = new HashMap(); List networkIds = _networksDao.findNetworksToGarbageCollect(); for (Long networkId : networkIds) { Long time = _lastNetworkIdsToFree.remove(networkId); if (time == null) { if (s_logger.isDebugEnabled()) { s_logger.debug("We found network " + networkId + " to be free for the first time. Adding it to the list: " + currentTime); } stillFree.put(networkId, currentTime); } else if (time > (currentTime - _networkGcWait)) { if (s_logger.isDebugEnabled()) { s_logger.debug("Network " + networkId + " is still free but it's not time to shutdown yet: " + time); } stillFree.put(networkId, time); } else { shutdownList.add(networkId); } } _lastNetworkIdsToFree = stillFree; for (Long networkId : shutdownList) { try { shutdownNetwork(networkId); } catch (Exception e) { s_logger.warn("Unable to shutdown network: " + networkId); } } } catch (Exception e) { s_logger.warn("Caught exception while running network gc: ", e); } } } @Override public boolean restartNetwork(RestartNetworkCmd cmd) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // This method restarts all network elements belonging to the network Long networkId = cmd.getNetworkId(); Network network = _networksDao.findById(networkId); Account owner = _accountMgr.getAccount(network.getAccountId()); User caller = _accountMgr.getActiveUser(UserContext.current().getCallerUserId()); Account callerAccount = _accountMgr.getActiveAccount(caller.getAccountId()); ReservationContext context = new ReservationContextImpl(null, null, caller, owner); _accountMgr.checkAccess(callerAccount, network); s_logger.debug("Restarting network " + networkId + "..."); boolean success = true; for (NetworkElement element : _networkElements) { success = element.restart(network, context); if (!success) { s_logger.warn("Failed to restart network element " + element + " as a part of network restart"); return success; } } s_logger.debug("Network " + networkId + " is restarted successfully."); return true; } @Override public int getActiveNicsInNetwork(long networkId) { return _networksDao.getActiveNicsIn(networkId); } @Override public Map> getZoneCapabilities(long zoneId) { DataCenterVO dc = _dcDao.findById(zoneId); if (dc == null) { throw new InvalidParameterValueException("Zone id=" + zoneId + " doesn't exist in the system."); } // Get all service providers from the datacenter Map providers = new HashMap(); providers.put(Service.Firewall, dc.getFirewallProvider()); providers.put(Service.Lb, dc.getLoadBalancerProvider()); providers.put(Service.Vpn, dc.getVpnProvider()); providers.put(Service.Dns, dc.getDnsProvider()); providers.put(Service.Gateway, dc.getGatewayProvider()); providers.put(Service.UserData, dc.getUserDataProvider()); providers.put(Service.Dhcp, dc.getDhcpProvider()); Map> networkCapabilities = new HashMap>(); for (NetworkElement element : _networkElements) { if (providers.isEmpty()) { break; } Map> elementCapabilities = element.getCapabilities(); if (elementCapabilities != null) { Iterator it = providers.keySet().iterator(); while (it.hasNext()) { Service service = it.next(); String zoneProvider = providers.get(service); if (zoneProvider != null) { if (zoneProvider.equalsIgnoreCase(element.getProvider().getName())) { if (elementCapabilities.containsKey(service)) { Map capabilities = elementCapabilities.get(service); // Verify if Service support capability if (capabilities != null) { for (Capability capability : capabilities.keySet()) { assert (service.containsCapability(capability)) : "Capability " + capability.getName() + " is not supported by the service " + service.getName(); } } networkCapabilities.put(service, capabilities); it.remove(); } } } } } } return networkCapabilities; } @Override public Network getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) { // find system public network offering Long networkOfferingId = null; List offerings = _networkOfferingDao.listSystemNetworkOfferings(); for (NetworkOfferingVO offering : offerings) { if (offering.getTrafficType() == trafficType) { networkOfferingId = offering.getId(); break; } } if (networkOfferingId == null) { throw new InvalidParameterValueException("Unable to find system network offering with traffic type " + trafficType); } List networks = _networksDao.listBy(Account.ACCOUNT_ID_SYSTEM, networkOfferingId, zoneId); if (networks == null) { throw new InvalidParameterValueException("Unable to find network with traffic type " + trafficType + " in zone " + zoneId); } return networks.get(0); } @Override public PublicIpAddress getPublicIpAddress(Ip ip) { IPAddressVO addr = _ipAddressDao.findById(ip); if (addr == null) { return null; } return new PublicIp(addr, _vlanDao.findById(addr.getVlanId()), NetUtils.createSequenceBasedMacAddress(addr.getMacAddress())); } @Override public List listPodVlans(long podId) { List vlans = _vlanDao.listVlansForPodByType(podId, VlanType.DirectAttached); return vlans; } @Override public List listNetworksUsedByVm(long vmId, boolean isSystem) { List networks = new ArrayList(); List nics = _nicDao.listBy(vmId); if (nics != null) { for (Nic nic : nics) { NetworkVO network = _networksDao.findByIdIncludingRemoved(nic.getNetworkId()); NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); if (no.isSystemOnly() == isSystem) { networks.add(network); } } } return networks; } @Override public Nic getNicInNetwork(long vmId, long networkId) { return _nicDao.findByInstanceIdAndNetworkId(networkId, vmId); } @Override @DB public boolean associateIpAddressListToAccount(long userId, long accountId, long zoneId, Long vlanId) throws InsufficientAddressCapacityException, ConcurrentOperationException, ResourceUnavailableException { Account account = _accountMgr.getActiveAccount(accountId); if (account == null) { s_logger.warn("Unable to find active account: " + accountId); } Network network = null; long allocatedIps = 0; //create new Virtual network for the user if it doesn't exist List networks = getVirtualNetworksOwnedByAccountInZone(account.getAccountName(), account.getDomainId(), zoneId); if (networks.size() == 0) { List offerings = _configMgr.listNetworkOfferings(TrafficType.Guest, false); network = createNetwork(offerings.get(0).getId(), account.getAccountName() + "-network", account.getAccountName() + "-network", false, null, zoneId, null, null, null, null, null, null, account); if (network == null){ s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId); return false; } else { //sourceNat ip is allocated as a part of networkCreate allocatedIps++; } } else { assert (networks.size() <= 1) : "Too many virtual networks. This logic should be obsolete"; network = networks.get(0); } //Associate ip addresses long ipCount = _ipAddressDao.countIPs(zoneId, vlanId, false); if (ipCount > 0) { while (allocatedIps < ipCount) { fetchNewPublicIp(zoneId, null, vlanId, account, VlanType.VirtualNetwork, network.getId(), false, true); allocatedIps++; } if (network.getState() == Network.State.Implemented) { s_logger.debug("Applying ip associations for vlan id=" + vlanId + " in network " + network); return applyIpAssociations(network, false); } else { s_logger.trace("Network id=" + network.getId() + " is not Implemented, no need to apply ipAssociations"); return true; } } else { s_logger.trace("Found 0 ips to assign in vlan id=" + vlanId); return true; } } @Override public Nic getNicForTraffic(long vmId, TrafficType type) { SearchCriteria sc = NicForTrafficTypeSearch.create(); sc.setParameters("instance", vmId); sc.setJoinParameters("network", "traffictype", type); List vos = _nicDao.search(sc, null); assert vos.size() <= 1 : "If we have multiple networks of the same type, then this method should no longer be used."; return vos.size() == 1 ? vos.get(0) : null; } @Override public IpAddress getIp(Ip ip) { return _ipAddressDao.findById(ip); } @Override public NetworkProfile getNetworkProfile(long networkId) { NetworkVO network = _networksDao.findById(networkId); NetworkGuru concierge = _networkGurus.get(network.getGuruName()); NetworkProfile profile = new NetworkProfile(network, null, null); concierge.updateNetworkProfile(profile); return profile; } @Override public Network getDefaultNetworkForVm(long vmId) { Nic defaultNic = getDefaultNic(vmId); if (defaultNic == null) { return null; } else { return _networksDao.findById(defaultNic.getNetworkId()); } } @Override public Nic getDefaultNic(long vmId) { List nics = _nicDao.listBy(vmId); Nic defaultNic = null; if (nics != null) { for (Nic nic: nics) { if (nic.isDefaultNic()) { defaultNic = nic; break; } } } else { s_logger.debug("Unable to find default network for the vm; vm doesn't have any nics"); return null; } if (defaultNic == null) { s_logger.debug("Unable to find default network for the vm; vm doesn't have default nic"); } return defaultNic; } @Override public List getPasswordResetElements() { List elements = new ArrayList(); for (NetworkElement element : _networkElements) { if (element instanceof PasswordResetElement) { elements.add((PasswordResetElement) element); } } return elements; } }