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 org.apache.cloudstack.network.router.deployment;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  
  import com.cloud.dc.DataCenter;
  import com.cloud.network.dao.IPAddressVO;
  
  import com.cloud.dc.dao.VlanDao;
  import com.cloud.deploy.DataCenterDeployment;
  import com.cloud.deploy.DeployDestination;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.exception.InsufficientCapacityException;
  import com.cloud.exception.ResourceUnavailableException;
  import com.cloud.network.Network;
  import com.cloud.network.PhysicalNetwork;
  import com.cloud.network.PhysicalNetworkServiceProvider;
  import com.cloud.network.VirtualRouterProvider.Type;
  import com.cloud.network.vpc.Vpc;
  import com.cloud.network.vpc.VpcManager;
  import com.cloud.network.vpc.dao.VpcDao;
  import com.cloud.network.vpc.dao.VpcOfferingDao;
  import com.cloud.user.Account;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.vm.DomainRouterVO;
  import com.cloud.vm.VirtualMachineProfile.Param;
  
  public class VpcRouterDeploymentDefinition extends RouterDeploymentDefinition {
  
      protected VpcDao vpcDao;
      protected VpcOfferingDao vpcOffDao;
      protected VpcManager vpcMgr;
      protected VlanDao vlanDao;
  
      protected Vpc vpc;
  
      protected VpcRouterDeploymentDefinition(final Network guestNetwork, final Vpc vpc, final DeployDestination dest, final Account owner,
              final Map<Param, Object> params) {
  
          super(guestNetwork, dest, owner, params);
  
          this.vpc = vpc;
      }
  
      @Override
      public Vpc getVpc() {
          return vpc;
      }
  
      @Override
      public boolean isVpcRouter() {
          return true;
      }
  
      @Override
      protected void lock() {
          final Vpc vpcLock = vpcDao.acquireInLockTable(vpc.getId());
          if (vpcLock == null) {
              throw new ConcurrentOperationException(String.format("Unable to lock vpc %s", vpc));
          }
          tableLockId = vpcLock.getId();
      }
  
      @Override
      protected void unlock() {
          if (tableLockId != null) {
              vpcDao.releaseFromLockTable(tableLockId);
              if (logger.isDebugEnabled()) {
                  logger.debug(String.format("Lock is released for vpc [id: %d] (%s) as a part of router startup in %s", tableLockId, vpc, dest));
              }
          }
      }
  
      @Override
      protected void checkPreconditions() {
          // No preconditions for Vpc
      }
  
      @Override
      protected List<DeployDestination> findDestinations() {
          final List<DeployDestination> destinations = new ArrayList<>();
          destinations.add(dest);
          return destinations;
      }
  
      /**
       * @see RouterDeploymentDefinition#prepareDeployment()
       *
       * @return if the deployment can proceed
       */
      @Override
      protected boolean prepareDeployment() {
          //Check if the VR is the src NAT provider...
          isPublicNetwork = vpcMgr.isSrcNatIpRequired(vpc.getVpcOfferingId());
  
          // Check if public network has to be set on VR
          return true;
      }
  
      @Override
      protected void findSourceNatIP() throws InsufficientAddressCapacityException, ConcurrentOperationException {
          sourceNatIp = null;
          DataCenter zone = dest.getDataCenter();
          Long zoneId = null;
          if (Objects.nonNull(zone)) {
              zoneId = zone.getId();
          }
  
          boolean isExternalProvider = isExternalProviderPresent(zoneId);
  
          if (isPublicNetwork) {
              if (!isExternalProvider) {
                  sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, null);
              } else {
                  sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc, getPodId());
                  if (vpc != null) {
                      IPAddressVO routerPublicIp = ipAddressDao.findByIp(sourceNatIp.getAddress().toString());
                      routerPublicIp.setVpcId(vpc.getId());
                      routerPublicIp.setSourceNat(true);
                      ipAddressDao.persist(routerPublicIp);
                  }
              }
          }
      }
  
      @Override
      protected void findOrDeployVirtualRouter() throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException {
          final Vpc vpc = getVpc();
          if (vpc != null) {
              // This call will associate any existing router to the "routers" attribute.
              // It's needed in order to continue with the VMs deployment.
              planDeploymentRouters();
              if (routers.size() == MAX_NUMBER_OF_ROUTERS) {
                  // If we have 2 routers already deployed, do nothing and return.
                  return;
              }
          }
          super.findOrDeployVirtualRouter();
      }
  
      @Override
      protected void findVirtualProvider() {
          final List<? extends PhysicalNetwork> pNtwks = pNtwkDao.listByZone(vpc.getZoneId());
  
          for (final PhysicalNetwork pNtwk : pNtwks) {
              final PhysicalNetworkServiceProvider provider = physicalProviderDao.findByServiceProvider(pNtwk.getId(), Type.VPCVirtualRouter.toString());
              if (provider == null) {
                  throw new CloudRuntimeException(String.format("Cannot find service provider %s in physical network %s", Type.VPCVirtualRouter.toString(), pNtwk));
              }
              vrProvider = vrProviderDao.findByNspIdAndType(provider.getId(), Type.VPCVirtualRouter);
              if (vrProvider != null) {
                  break;
              }
          }
      }
  
      @Override
      protected void findServiceOfferingId() {
          serviceOfferingId = vpcOffDao.findById(vpc.getVpcOfferingId()).getServiceOfferingId();
          if (serviceOfferingId == null) {
              findAccountServiceOfferingId(vpc.getAccountId());
          }
          if (serviceOfferingId == null) {
              findDefaultServiceOfferingId();
          }
      }
  
      @Override
      protected void deployAllVirtualRouters() throws ConcurrentOperationException, InsufficientCapacityException,
      ResourceUnavailableException {
  
          // Implement Redundant Vpc
          final int routersToDeploy = getNumberOfRoutersToDeploy();
          for(int i = 0; i < routersToDeploy; i++) {
              // Don't start the router as we are holding the network lock that needs to be released at the end of router allocation
              final DomainRouterVO router = nwHelper.deployRouter(this, false);
  
              if (router != null) {
                  routers.add(router);
              }
          }
      }
  
      @Override
      protected void planDeploymentRouters() {
          routers = routerDao.listByVpcId(vpc.getId());
      }
  
      @Override
      public void generateDeploymentPlan() {
          plan = new DataCenterDeployment(dest.getDataCenter().getId());
      }
  
      @Override
      public boolean isRedundant() {
          return vpc.isRedundant();
      }
  
      @Override
      public boolean isRollingRestart() {
          return vpc.isRollingRestart();
      }
  }
  