content: // 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;
  
  import java.util.List;
  
  import javax.inject.Inject;
  
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.engine.cloud.entity.api.db.VMNetworkMapVO;
  import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMNetworkMapDao;
  import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
  import org.apache.cloudstack.resourcedetail.FirewallRuleDetailVO;
  import org.apache.cloudstack.resourcedetail.VpcDetailVO;
  import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao;
  import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao;
  
  import com.cloud.configuration.Resource;
  import com.cloud.dc.DataCenter;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.deploy.DataCenterDeployment;
  import com.cloud.deploy.DeployDestination;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.exception.InsufficientCapacityException;
  import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.exception.ResourceAllocationException;
  import com.cloud.exception.ResourceUnavailableException;
  import com.cloud.host.Host;
  import com.cloud.host.dao.HostDao;
  import com.cloud.network.dao.FirewallRulesCidrsDao;
  import com.cloud.network.dao.FirewallRulesCidrsVO;
  import com.cloud.network.dao.FirewallRulesDao;
  import com.cloud.network.dao.IPAddressDao;
  import com.cloud.network.dao.IPAddressVO;
  import com.cloud.network.dao.NetworkDao;
  import com.cloud.network.dao.NetworkDetailVO;
  import com.cloud.network.dao.NetworkDetailsDao;
  import com.cloud.network.dao.NetworkDomainDao;
  import com.cloud.network.dao.NetworkDomainVO;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.dao.RouterNetworkDao;
  import com.cloud.network.dao.RouterNetworkVO;
  import com.cloud.network.guru.NetworkGuru;
  import com.cloud.network.router.VirtualRouter;
  import com.cloud.network.rules.FirewallRule;
  import com.cloud.network.rules.FirewallRuleVO;
  import com.cloud.network.rules.RulesManager;
  import com.cloud.network.vpc.NetworkACLVO;
  import com.cloud.network.vpc.Vpc;
  import com.cloud.network.vpc.VpcGatewayVO;
  import com.cloud.network.vpc.VpcManager;
  import com.cloud.network.vpc.VpcService;
  import com.cloud.network.vpc.VpcVO;
  import com.cloud.network.vpc.dao.NetworkACLDao;
  import com.cloud.network.vpc.dao.VpcDao;
  import com.cloud.network.vpc.dao.VpcGatewayDao;
  import com.cloud.offering.NetworkOffering;
  import com.cloud.offerings.dao.NetworkOfferingDao;
  import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
  import com.cloud.server.ResourceTag;
  import com.cloud.tags.ResourceTagVO;
  import com.cloud.tags.dao.ResourceTagDao;
  import com.cloud.user.Account;
  import com.cloud.user.AccountVO;
  import com.cloud.user.ResourceLimitService;
  import com.cloud.user.dao.AccountDao;
  import com.cloud.utils.db.DB;
  import com.cloud.utils.db.EntityManager;
  import com.cloud.utils.db.Transaction;
  import com.cloud.utils.db.TransactionCallback;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.vm.DomainRouterVO;
  import com.cloud.vm.Nic;
  import com.cloud.vm.NicDetailVO;
  import com.cloud.vm.NicProfile;
  import com.cloud.vm.NicVO;
  import com.cloud.vm.ReservationContext;
  import com.cloud.vm.ReservationContextImpl;
  import com.cloud.vm.UserVmVO;
  import com.cloud.vm.VirtualMachine;
  import com.cloud.vm.VirtualMachineManager;
  import com.cloud.vm.VirtualMachineProfile;
  import com.cloud.vm.VirtualMachineProfileImpl;
  import com.cloud.vm.dao.DomainRouterDao;
  import com.cloud.vm.dao.NicDao;
  import com.cloud.vm.dao.NicDetailsDao;
  import com.cloud.vm.dao.NicIpAliasDao;
  import com.cloud.vm.dao.NicSecondaryIpDao;
  import com.cloud.vm.dao.UserVmDao;
  
  public class NetworkMigrationManagerImpl implements NetworkMigrationManager {
      protected Logger logger = LogManager.getLogger(getClass());
  
      @Inject
      private DataCenterDao _dcDao = null;
      @Inject
      private NetworkDetailsDao _networkDetailsDao = null;
      @Inject
      private AccountDao _accountDao = null;
      @Inject
      private NetworkDomainDao _networkDomainDao = null;
      @Inject
      private NetworkOrchestrationService _networkMgr = null;
      @Inject
      private ResourceLimitService _resourceLimitMgr = null;
      @Inject
      private NetworkDao _networksDao = null;
      @Inject
      private NetworkOfferingDao _networkOfferingDao = null;
      @Inject
      private NetworkOfferingServiceMapDao _networkOfferingServiceDao = null;
      @Inject
      private NicDao _nicDao = null;
      @Inject
      private NicSecondaryIpDao _nicSecondaryIpDao = null;
      @Inject
      private NicIpAliasDao _nicIpAliasDao = null;
      @Inject
      private NicDetailsDao _nicDetailsDao = null;
      @Inject
      private FirewallRulesDao _firewallDao = null;
      @Inject
      private FirewallRulesCidrsDao _firewallRulesCidrDao = null;
      @Inject
      private FirewallRuleDetailsDao _firewallRuleDetailsDao = null;
      @Inject
      private EntityManager _entityMgr = null;
      @Inject
      private RouterNetworkDao _routerNetworkDao = null;
      @Inject
      private DomainRouterDao _routerDao = null;
      @Inject
      private NetworkService _networkService = null;
      @Inject
      private UserVmDao _vmDao = null;
      @Inject
      private NetworkModel _networkModel= null;
      @Inject
      private VMNetworkMapDao _vmNetworkMapDao = null;
      @Inject
      private HostDao _hostDao = null;
      @Inject
      private VirtualMachineManager _itMgr = null;
      @Inject
      private IPAddressDao _ipAddressDao = null;
      @Inject
      private RulesManager _rulesMgr = null;
      @Inject
      private VpcDao _vpcDao = null;
      @Inject
      private VpcDetailsDao _vpcDetailsDao = null;
      @Inject
      private IpAddressManager _ipAddressManager = null;
      @Inject
      private VpcService _vpcService = null;
      @Inject
      private NetworkACLDao _networkACLDao = null;
      @Inject
      private VpcManager _vpcManager = null;
      @Inject
      private VpcGatewayDao _vpcGatewayDao = null;
      @Inject
      private ResourceTagDao _resourceTagDao = null;
  
      @Override public long makeCopyOfNetwork(Network network, NetworkOffering networkOffering, Long vpcId) {
          if (logger.isDebugEnabled()) {
              logger.debug("Making a copy of network {} for migration.", network);
          }
          long originalNetworkId = network.getId();
          NetworkDomainVO domainNetworkMapByNetworkId = _networkDomainDao.getDomainNetworkMapByNetworkId(originalNetworkId);
          AccountVO networkAccount = _accountDao.findById(network.getAccountId());
  
          boolean subdomainAccess = true;
          if (domainNetworkMapByNetworkId != null) {
              subdomainAccess = domainNetworkMapByNetworkId.isSubdomainAccess();
          }
          DataCenterDeployment plan = new DataCenterDeployment(network.getDataCenterId(), null, null, null, null, network.getPhysicalNetworkId());
  
          List<? extends Network> networks = _networkMgr.setupNetwork(networkAccount, networkOffering, network, plan, network.getName(), network.getDisplayText(), true,
                                                                      network.getDomainId(), network.getAclType(), subdomainAccess,
                                                                      vpcId, true);
          _resourceLimitMgr.incrementResourceCount(network.getAccountId(), Resource.ResourceType.network, network.isDisplay());
  
          long networkCopyId;
          if (networks == null || networks.isEmpty()) {
              throw new CloudRuntimeException("Fail to create a network");
          } else {
              DataCenter zone = _dcDao.findById(network.getDataCenterId());
              String guestNetworkCidr = zone.getGuestNetworkCidr();
  
              if (networks.get(0).getGuestType() == Network.GuestType.Isolated
                      && networks.get(0).getTrafficType() == Networks.TrafficType.Guest) {
                  Network networkCopy = networks.get(0);
                  for (final Network nw : networks) {
                      if (nw.getCidr() != null && nw.getCidr().equals(guestNetworkCidr)) {
                          networkCopy = nw;
                      }
                  }
                  networkCopyId = networkCopy.getId();
              } else {
                  // For shared network
                  networkCopyId = networks.get(0).getId();
              }
          }
  
          //Update the related network
          NetworkVO originalNetwork = _networksDao.findById(originalNetworkId);
          originalNetwork.setRelated(networkCopyId);
          _networksDao.update(originalNetworkId, originalNetwork);
  
          NetworkVO copiedNetwork = _networksDao.findById(networkCopyId);
          copiedNetwork.setRelated(originalNetworkId);
          copiedNetwork.setDisplayNetwork(false);
          copiedNetwork.setBroadcastUri(network.getBroadcastUri());
          copiedNetwork.setState(network.getState());
          _networksDao.update(networkCopyId, copiedNetwork);
  
          copyNetworkDetails(originalNetworkId, networkCopyId);
          copyFirewallRulesToNewNetwork(network, copiedNetwork);
          assignUserNicsToNewNetwork(originalNetworkId, networkCopyId);
          assignRouterNicsToNewNetwork(network.getId(), networkCopyId);
  
          if (logger.isDebugEnabled()) {
              logger.debug("Successfully created a copy of network  " + originalNetwork.getName() + "(" + originalNetwork.getUuid() + ") id is " + originalNetwork.getId() + " for migration. The network copy has uuid " + network.getUuid() + " and id " + network.getId());
          }
          return networkCopyId;
      }
  
      @DB
      private void copyNetworkDetails(long srcNetworkId, long dstNetworkId) {
          List<NetworkDetailVO> networkDetails = _networkDetailsDao.listDetails(srcNetworkId);
  
          for (NetworkDetailVO networkDetail : networkDetails) {
              _networkDetailsDao.persist(new NetworkDetailVO(dstNetworkId, networkDetail.getName(), networkDetail.getValue(), networkDetail.isDisplay()));
          }
      }
  
      /**
       * reassigns the nics to the new network from the src network.
       * @param srcNetworkId
       * @param dstNetworkId
       */
      private void assignUserNicsToNewNetwork(long srcNetworkId, long dstNetworkId) {
          List<NicVO> nics = _nicDao.listByNetworkId(srcNetworkId);
  
          for (NicVO nic : nics) {
              if (nic.getVmType() == VirtualMachine.Type.User) {
                  nic.setNetworkId(dstNetworkId);
                  _nicDao.persist(nic);
  
                  //update the number of active nics in both networks after migration.
                  if (nic.getState() == Nic.State.Reserved) {
                      _networksDao.changeActiveNicsBy(srcNetworkId, -1);
                      _networksDao.changeActiveNicsBy(dstNetworkId, 1);
                  }
              }
          }
  
          List<? extends IPAddressVO> publicIps = _ipAddressDao.listByAssociatedNetwork(srcNetworkId, null);
  
          for (IPAddressVO ipAddress : publicIps) {
                  ipAddress.setAssociatedWithNetworkId(dstNetworkId);
                  _ipAddressDao.persist(ipAddress);
          }
      }
  
      @Override
      public Long makeCopyOfVpc(long vpcId, long vpcOfferingId) {
          VpcVO vpc = _vpcDao.findById(vpcId);
          if (logger.isDebugEnabled()) {
              logger.debug("Making a copy of vpc {} for migration.", vpc);
          }
          if (vpc == null) {
              InvalidParameterValueException ex = new InvalidParameterValueException("Specified vpc id doesn't exist in the system");
              ex.addProxyObject(String.valueOf(vpcId), "vpcId");
              throw ex;
          }
  
          Vpc copyOfVpc;
          long copyOfVpcId;
          try {
  
              copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(),
                      vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
                      vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu(), null, null, null, vpc.useRouterIpAsResolver());
  
              copyOfVpcId = copyOfVpc.getId();
              //on resume of migration the uuid will be swapped already. So the copy will have the value of the original vpcid.
              _resourceTagDao.persist(new ResourceTagVO(MIGRATION, Long.toString(vpcId), vpc.getAccountId(), vpc.getDomainId(), copyOfVpcId, ResourceTag.ResourceObjectType.Vpc, null, vpc.getUuid()));
              VpcVO copyVpcVO = _vpcDao.findById(copyOfVpcId);
              vpc.setDisplay(false);
              swapUuids(vpc, copyVpcVO);
              reassignACLRulesToNewVpc(vpcId, copyOfVpcId);
              reassignPublicIpsToNewVpc(vpcId, copyOfVpc);
              copyVpcDetails(vpcId, copyOfVpcId);
              reassignGatewayToNewVpc(vpcId, copyOfVpcId);
              copyVpcResourceTagsToNewVpc(vpcId, copyOfVpcId);
              if (logger.isDebugEnabled()) {
                  logger.debug("Successfully created a copy of network  " + vpc.getName() + "(" + vpc.getUuid() + ") id is " + vpc.getId() + " for migration. The network copy has uuid " + copyVpcVO.getUuid() + " and id " + copyOfVpc.getId());
              }
          } catch (ResourceAllocationException e) {
              throw new CloudRuntimeException(e.getMessage());
          }
  
          return copyOfVpcId;
      }
  
      @Override
      public void startVpc(Vpc vpc) {
          if (vpc.getState() != Vpc.State.Enabled) {
              try {
                  _vpcService.startVpc(vpc.getId(), true);
              } catch (ResourceUnavailableException | InsufficientCapacityException e) {
                  logger.error("Vpc can not be started. Aborting migration process");
                  throw new CloudRuntimeException("Vpc can not be started.", e);
              }
          }
      }
  
  
      private void copyVpcDetails(long srcVpcId, long dstVpcId) {
          List<VpcDetailVO> vpcDetails = _vpcDetailsDao.listDetails(srcVpcId);
  
          for (VpcDetailVO vpcDetail : vpcDetails) {
              _vpcDetailsDao.persist(new VpcDetailVO(dstVpcId, vpcDetail.getName(), vpcDetail.getValue(), vpcDetail.isDisplay()));
          }
      }
  
      /**
       * Reassign the ACL rules from the original vpc to the new VPC
       * @param srcVpcId
       * @param dstVpcId
       */
      private void reassignACLRulesToNewVpc(long srcVpcId, long dstVpcId){
          List<NetworkACLVO> networkACL = _networkACLDao.listByVpcId(srcVpcId);
  
          for (NetworkACLVO aclList : networkACL) {
              aclList.setVpcId(dstVpcId);
              _networkACLDao.persist(aclList);
          }
      }
  
      private void reassignPublicIpsToNewVpc(long srcVpcId, Vpc dstVpc) {
          List<? extends IPAddressVO> publicIps = _ipAddressDao.listByAssociatedVpc(srcVpcId, _vpcManager.isSrcNatIpRequired(dstVpc.getVpcOfferingId()) ? null : false);
  
          for(IPAddressVO publicIp : publicIps) {
              publicIp.setVpcId(dstVpc.getId());
              _ipAddressDao.persist(publicIp);
          }
      }
  
      private void reassignGatewayToNewVpc(long srcVpcId, long dstVpcId){
          List<VpcGatewayVO> vpcGateways = _vpcGatewayDao.listByVpcId(srcVpcId);
          for (VpcGatewayVO vpcGateway : vpcGateways) {
              vpcGateway.setVpcId(dstVpcId);
              _vpcGatewayDao.persist(vpcGateway);
          }
      }
  
      private void copyVpcResourceTagsToNewVpc(long srcVpcId, long dstVpcId){
          List<? extends ResourceTag> resourceTags = _resourceTagDao.listBy(srcVpcId, ResourceTag.ResourceObjectType.Vpc);
          for (ResourceTag resourceTag : resourceTags) {
              resourceTag.setResourceId(dstVpcId);
              _resourceTagDao.persist(
                      new ResourceTagVO(
                              resourceTag.getKey(),
                              resourceTag.getValue(),
                              resourceTag.getAccountId(),
                              resourceTag.getDomainId(),
                              dstVpcId,
                              resourceTag.getResourceType(),
                              resourceTag.getCustomer(),
                              resourceTag.getResourceUuid()));
          }
      }
  
      private void copyFirewallRulesToNewNetwork(Network srcNetwork, Network dstNetwork) {
          List<FirewallRuleVO> firewallRules = _firewallDao.listByNetworkPurposeTrafficType(srcNetwork.getId(), FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Egress);
          firewallRules.addAll(_firewallDao.listByNetworkPurposeTrafficType(srcNetwork.getId(), FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress));
          if (logger.isDebugEnabled()) {
              logger.debug("Copying firewall rules from network {} to network {}", srcNetwork, dstNetwork);
          }
  
          //Loop over all the firewall rules in the original network and copy all values to a new firewall rule
          //Copy all objects that are dependant on the firewall rules
          for (FirewallRuleVO originalFirewallRule : firewallRules) {
              FirewallRuleVO ruleVO = new FirewallRuleVO(originalFirewallRule.getXid(),
                                                         originalFirewallRule.getSourceIpAddressId(),
                                                         originalFirewallRule.getSourcePortStart(),
                                                         originalFirewallRule.getSourcePortEnd(),
                                                         originalFirewallRule.getProtocol(),
                                                         dstNetwork.getId(),
                                                         srcNetwork.getAccountId(),
                                                         srcNetwork.getDomainId(),
                                                         originalFirewallRule.getPurpose(),
                                                         originalFirewallRule.getSourceCidrList(),
                                                         originalFirewallRule.getDestinationCidrList(),
                                                         originalFirewallRule.getIcmpCode(),
                                                         originalFirewallRule.getIcmpType(),
                                                         originalFirewallRule.getRelated(),
                                                         originalFirewallRule.getTrafficType(),
                                                         originalFirewallRule.getType());
  
              ruleVO = _firewallDao.persist(ruleVO);
  
              //Firewall rule cidrs
              List<FirewallRulesCidrsVO> firewallRulesCidrsVOS = _firewallRulesCidrDao.listByFirewallRuleId(originalFirewallRule.getId());
              for (FirewallRulesCidrsVO firewallRulesCidrVO: firewallRulesCidrsVOS) {
                  _firewallRulesCidrDao.persist(new FirewallRulesCidrsVO(ruleVO.getId(), firewallRulesCidrVO.getSourceCidrList()));
              }
  
              //Firewall rules details
              List<FirewallRuleDetailVO> originalFirewallRuleDetails = _firewallRuleDetailsDao.listDetails(originalFirewallRule.getId());
              for (FirewallRuleDetailVO originalFirewallRuleDetail : originalFirewallRuleDetails) {
                  _firewallRuleDetailsDao.persist(new FirewallRuleDetailVO(ruleVO.getId(), originalFirewallRuleDetail.getName(), originalFirewallRuleDetail.getValue(), originalFirewallRuleDetail.isDisplay()));
              }
          }
      }
  
      private void assignRouterNicsToNewNetwork(long srcNetworkId, long dstNetworkId) {
          final List<DomainRouterVO> routers = _routerDao.listByNetworkAndRole(srcNetworkId, VirtualRouter.Role.VIRTUAL_ROUTER);
          for (DomainRouterVO domainRouter : routers) {
              NicVO vrNic = _nicDao.findByNetworkIdAndType(srcNetworkId, VirtualMachine.Type.DomainRouter);
              vrNic.setNetworkId(dstNetworkId);
              _nicDao.update(vrNic.getId(), vrNic);
  
              RouterNetworkVO routerNetwork = _routerNetworkDao.findByRouterAndNetwork(domainRouter.getId(), srcNetworkId);
              routerNetwork.setNetworkId(dstNetworkId);
              _routerNetworkDao.persist(routerNetwork);
          }
      }
  
      @Override
      public Network upgradeNetworkToNewNetworkOffering(long networkId, long newPhysicalNetworkId, long networkOfferingId, Long vpcId) {
          logger.debug("upgrading network to network with new offering.");
          NetworkVO network = _networksDao.findById(networkId);
          NetworkOffering newOffering = _networkOfferingDao.findByIdIncludingRemoved(networkOfferingId);
          long gurusImplementing = 0;
          network.setBroadcastUri(null);
          AccountVO networkAccount = _accountDao.findById(network.getAccountId());
          DataCenterDeployment plan = new DataCenterDeployment(network.getDataCenterId(), null, null, null, null, newPhysicalNetworkId);
          for (final NetworkGuru guru : _networkMgr.getNetworkGurus()) {
  
              final Network designedNetwork = guru.design(newOffering, plan, network, network.getName(), vpcId, networkAccount);
              if (designedNetwork == null) {
                  continue;
              }
  
              gurusImplementing++;
              if (gurusImplementing > 1) {
                  throw new CloudRuntimeException("Failed to migrate network to new physical network. Multiple network guru's for the same network are currently not supported.");
              }
  
              network.setTrafficType(designedNetwork.getTrafficType());
              network.setMode(designedNetwork.getMode());
              network.setBroadcastDomainType(designedNetwork.getBroadcastDomainType());
              network.setBroadcastUri(designedNetwork.getBroadcastUri());
              network.setNetworkOfferingId(designedNetwork.getNetworkOfferingId());
              network.setState(designedNetwork.getState());
              network.setPhysicalNetworkId(designedNetwork.getPhysicalNetworkId());
              network.setRedundant(designedNetwork.isRedundant());
              network.setGateway(designedNetwork.getGateway());
              network.setCidr(designedNetwork.getCidr());
              network.setGuruName(guru.getName());
              network.setVpcId(vpcId);
          }
          _networksDao.update(network.getId(), network, _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), newPhysicalNetworkId));
          return network;
      }
  
      @Override
      public void deleteCopyOfNetwork(long networkCopyId, long originalNetworkId) {
          NetworkVO networkCopy = _networksDao.findById(networkCopyId);
  
          NicVO userNic = _nicDao.findByNetworkIdAndType(networkCopyId, VirtualMachine.Type.User);
          if (userNic != null) {
              logger.error("Something went wrong while migrating nics from the old network to the new network. Failed to delete copy of network. There are still user nics present in the network.");
              throw new CloudRuntimeException("Failed to delete copy of network. There are still user nics present in the network.");
          }
  
          NetworkVO originalNetwork = _networksDao.findById(originalNetworkId);
  
          swapUuids(originalNetwork, networkCopy);
          try {
              if (!_networkService.deleteNetwork(networkCopy.getId(), true)) {
                  throw new CloudRuntimeException("Failed to delete network. Clean up not successful.");
              }
          } finally {
              swapUuids(networkCopy, originalNetwork);
          }
          originalNetwork.setRelated(originalNetworkId);
          _networksDao.persist(originalNetwork);
      }
  
      @Override
      public void deleteCopyOfVpc(long vpcCopyId, long originalVpcId) {
          VpcVO copyOfvpc = _vpcDao.findById(vpcCopyId);
          VpcVO originalVpc = _vpcDao.findById(originalVpcId);
  
          //Be sure that when we delete the vpc, it has the uuid with what it was created.
          swapUuids(copyOfvpc, originalVpc);
          try {
              if(!_vpcService.deleteVpc(vpcCopyId)) {
                  throw new CloudRuntimeException("Deletion of VPC failed. Clean up was not successful.");
              }
          } catch (ResourceUnavailableException e) {
              throw new CloudRuntimeException(e.getMessage());
          } finally {
              swapUuids(originalVpc, copyOfvpc);
          }
          _resourceTagDao.removeByResourceIdAndKey(originalVpcId, ResourceTag.ResourceObjectType.Vpc, MIGRATION);
      }
  
      private Boolean migrateNicsInDB(NicVO originalNic, Network networkInNewPhysicalNet, DataCenter dc, ReservationContext context) {
          logger.debug("migrating nics in database.");
          UserVmVO vmVO = _vmDao.findById(originalNic.getInstanceId());
          VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null);
          NicProfile nicProfile = new NicProfile(originalNic, networkInNewPhysicalNet, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(networkInNewPhysicalNet), null);
          try {
              nicProfile = _networkMgr.allocateNic(nicProfile, networkInNewPhysicalNet, originalNic.isDefaultNic(), nicProfile.getDeviceId(), vmProfile).first();
          } catch (InsufficientVirtualNetworkCapacityException | InsufficientAddressCapacityException e) {
              throw new CloudRuntimeException("Allocation of new nicProfile failed during migration", e);
          }
  
          //Update vm_network_map table
          if (vmProfile.getType() == VirtualMachine.Type.User) {
              final VMNetworkMapVO vno = new VMNetworkMapVO(vmVO.getId(), networkInNewPhysicalNet.getId());
              _vmNetworkMapDao.persist(vno);
          }
  
          NicVO newNic = _nicDao.findById(nicProfile.getId());
  
          copyNicDetails(originalNic.getId(), newNic.getId());
          //Update nic uuid here
          moveServices(originalNic, newNic);
  
          if (originalNic.getState() == Nic.State.Reserved) {
              final VirtualMachine vm = vmProfile.getVirtualMachine();
              final Host host = _hostDao.findById(vm.getHostId());
              final DeployDestination dest = new DeployDestination(dc, null, null, host);
  
              try {
                  nicProfile = _networkMgr.prepareNic(vmProfile, dest, context, nicProfile.getId(), networkInNewPhysicalNet);
                   _itMgr.replugNic(networkInNewPhysicalNet, _itMgr.toNicTO(nicProfile, host.getHypervisorType()), _itMgr.toVmTO(vmProfile), dest.getHost());
              } catch (ResourceUnavailableException | InsufficientCapacityException e) {
                  throw new CloudRuntimeException("Migration of Nic failed", e);
              }
          }
  
          //Mark the old nic as removed
          markAsNonDefault(originalNic);
          _networkMgr.removeNic(vmProfile, originalNic);
  
          if (logger.isDebugEnabled()) {
              logger.debug("Nic is migrated successfully for vm " + vmVO + " to " + networkInNewPhysicalNet);
          }
          return true;
      }
  
      @Override
      public void assignNicsToNewPhysicalNetwork(Network srcNetwork, Network networkInNewPhysicalNet) {
          List<NicVO> nics = _nicDao.listByNetworkId(srcNetwork.getId());
  
          final CallContext cctx = CallContext.current();
          final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount());
          final DataCenter dc = _entityMgr.findById(DataCenter.class, networkInNewPhysicalNet.getDataCenterId());
  
          //For each nic in the old network check if the nic belongs to a guest vm and migrate it to the new network.
          for (NicVO originalNic : nics) {
              if (!VirtualMachine.Type.User.equals(originalNic.getVmType())) {
                  continue;
              }
              Transaction.execute((TransactionCallback<Boolean>)
                                              (status) -> migrateNicsInDB(originalNic, networkInNewPhysicalNet, dc, context));
          }
  
          //Now that nics are migrated we can migrate the static nats on those nics
          reapplyPublicIps(srcNetwork, networkInNewPhysicalNet);
      }
  
      private void reapplyPublicIps(Network networkInOldPhysicalNetwork, Network networkInNewPhysicalNet) {
          CallContext ctx = CallContext.current();
          long callerUserId = ctx.getCallingUserId();
          Account caller = ctx.getCallingAccount();
  
          AccountVO networkAccount = _accountDao.findById(networkInNewPhysicalNet.getAccountId());
          List<? extends IPAddressVO> staticNatIps = _ipAddressDao.listStaticNatPublicIps(networkInOldPhysicalNetwork.getId());
  
          List<String> providers = _networkOfferingServiceDao.listProvidersForServiceForNetworkOffering(networkInNewPhysicalNet.getNetworkOfferingId(), Network.Service.SourceNat);
          boolean isSrcNatIpNeeded = providers.stream().anyMatch(provider -> provider.contains(Network.Provider.VirtualRouter.getName()));
  
          for (IPAddressVO ipAddress : staticNatIps) {
              if (!ipAddress.isSourceNat() || isSrcNatIpNeeded) {
                  ipAddress.setAssociatedWithNetworkId(networkInNewPhysicalNet.getId());
                  _ipAddressDao.persist(ipAddress);
              } else {
                  _ipAddressManager.disassociatePublicIpAddress(ipAddress, callerUserId, caller);
              }
          }
  
          _rulesMgr.applyStaticNatsForNetwork(networkInNewPhysicalNet, false, networkAccount);
      }
  
      private void copyNicDetails(long originalNicId, long dstNicId) {
          List<NicDetailVO> nicDetails = _nicDetailsDao.listDetails(originalNicId);
  
          for (NicDetailVO nicDetail : nicDetails) {
              _nicDetailsDao.persist(new NicDetailVO(dstNicId, nicDetail.getName(), nicDetail.getValue(), nicDetail.isDisplay()));
          }
      }
  
      private void moveServices(NicVO originalNic, NicVO newNic) {
          _nicIpAliasDao.moveIpAliases(originalNic.getId(), newNic.getId());
          _nicSecondaryIpDao.moveSecondaryIps(originalNic.getId(), newNic.getId());
          swapUuids(originalNic, newNic);
      }
  
      private void markAsNonDefault(NicVO nic) {
          nic.setDefaultNic(false);
          _nicDao.persist(nic);
      }
  
      /**
       * Swaps the UUID's of the given nics's
       * @param oldNic
       * @param newNic
       */
      private void swapUuids(NicVO oldNic, NicVO newNic) {
          final String realUuid = oldNic.getUuid();
          final String dummyUuid = newNic.getUuid();
  
          oldNic.setUuid(dummyUuid.replace("-", "+"));
          newNic.setUuid(realUuid);
          _nicDao.persist(oldNic);
          _nicDao.persist(newNic);
  
          oldNic.setUuid(dummyUuid);
          _nicDao.persist(oldNic);
      }
  
      /**
       * Swaps the UUID's of the given networks
       * @param oldNetwork
       * @param newNetwork
       */
      private void swapUuids(NetworkVO oldNetwork, NetworkVO newNetwork) {
          String realUuid = oldNetwork.getUuid();
          String dummyUuid = newNetwork.getUuid();
  
          oldNetwork.setUuid(dummyUuid.replace("-","+"));
          newNetwork.setUuid(realUuid);
          _networksDao.update(oldNetwork.getId(), oldNetwork);
          _networksDao.update(newNetwork.getId(), newNetwork);
  
          oldNetwork.setUuid(dummyUuid);
          _networksDao.update(oldNetwork.getId(), oldNetwork);
      }
  
      /**
       * Swaps the UUID's of the given vpcs
       * @param oldVpc
       * @param newVpc
       */
      private void swapUuids(VpcVO oldVpc, VpcVO newVpc) {
          String realUuid = oldVpc.getUuid();
          String dummyUuid = newVpc.getUuid();
  
          oldVpc.setUuid(dummyUuid.replace("-","+"));
          newVpc.setUuid(realUuid);
          _vpcDao.update(oldVpc.getId(), oldVpc);
          _vpcDao.update(newVpc.getId(), newVpc);
  
          oldVpc.setUuid(dummyUuid);
          _vpcDao.update(oldVpc.getId(), oldVpc);
      }
  
  }
  