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.router;
  
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.Set;
  import java.util.TreeSet;
  
  import javax.annotation.PostConstruct;
  import javax.inject.Inject;
  
  import org.apache.commons.lang3.StringUtils;
  import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
  
  import com.cloud.dc.dao.VlanDao;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.exception.InsufficientCapacityException;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.network.IpAddress;
  import com.cloud.network.Network;
  import com.cloud.network.Networks.BroadcastDomainType;
  import com.cloud.network.Networks.IsolationType;
  import com.cloud.network.addr.PublicIp;
  import com.cloud.network.dao.IPAddressVO;
  import com.cloud.network.vpc.PrivateGateway;
  import com.cloud.network.vpc.VpcManager;
  import com.cloud.offering.NetworkOffering;
  import com.cloud.service.ServiceOfferingVO;
  import com.cloud.storage.VMTemplateVO;
  import com.cloud.vm.NicProfile;
  
  
  public class VpcNetworkHelperImpl extends NetworkHelperImpl {
  
  
      @Inject
      private VlanDao _vlanDao;
      @Inject
      protected VpcManager vpcMgr;
      @Inject
      protected NicProfileHelper nicProfileHelper;
  
      protected String noHypervisorsErrMsgDetails;
  
      @PostConstruct
      protected void setupNoHypervisorsErrMsgDetails() {
          noHypervisorsErrMsgDetails = StringUtils.join(vpcMgr.getSupportedVpcHypervisors(), ',');
          noHypervisorsErrMsgDetails += " are the only supported Hypervisors";
      }
  
      @Override
      protected String getNoHypervisorsErrMsgDetails() {
          return noHypervisorsErrMsgDetails;
      }
  
      @Override
      protected void filterSupportedHypervisors(final List<HypervisorType> hypervisors) {
          hypervisors.retainAll(vpcMgr.getSupportedVpcHypervisors());
      }
  
      @Override
      public void reallocateRouterNetworks(final RouterDeploymentDefinition vpcRouterDeploymentDefinition, final VirtualRouter router, final VMTemplateVO template, final HypervisorType hType)
              throws ConcurrentOperationException, InsufficientCapacityException {
  
          final TreeSet<String> publicVlans = new TreeSet<String>();
          if (vpcRouterDeploymentDefinition.isPublicNetwork()) {
              String vlanTag = "";
              if (Objects.nonNull(vpcRouterDeploymentDefinition.getSourceNatIP().getVlanTag())) {
                  vlanTag = vpcRouterDeploymentDefinition.getSourceNatIP().getVlanTag();
              }
              publicVlans.add(vlanTag);
          }
  
          //1) allocate nic for control and source nat public ip
          final LinkedHashMap<Network, List<? extends NicProfile>> networks = configureDefaultNics(vpcRouterDeploymentDefinition);
  
          final Long vpcId = vpcRouterDeploymentDefinition.getVpc().getId();
          //2) allocate nic for private gateways if needed
          final List<PrivateGateway> privateGateways = vpcMgr.getVpcPrivateGateways(vpcId);
          if (privateGateways != null && !privateGateways.isEmpty()) {
              for (final PrivateGateway privateGateway : privateGateways) {
                  final NicProfile privateNic = nicProfileHelper.createPrivateNicProfileForGateway(privateGateway, router);
                  final Network privateNetwork = _networkModel.getNetwork(privateGateway.getNetworkId());
                  networks.put(privateNetwork, new ArrayList<NicProfile>(Arrays.asList(privateNic)));
              }
          }
  
          //3) allocate nic for guest gateway if needed
          final List<? extends Network> guestNetworks = vpcMgr.getVpcNetworks(vpcId);
          for (final Network guestNetwork : guestNetworks) {
              if (_networkModel.isPrivateGateway(guestNetwork.getId())) {
                  continue;
              }
              if (guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup) {
                  final NicProfile guestNic = nicProfileHelper.createGuestNicProfileForVpcRouter(vpcRouterDeploymentDefinition, guestNetwork);
                  networks.put(guestNetwork, new ArrayList<NicProfile>(Arrays.asList(guestNic)));
              }
          }
  
          //4) allocate nic for additional public network(s)
          final List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpcId, false);
          final List<NicProfile> publicNics = new ArrayList<NicProfile>();
          Network publicNetwork = null;
          final Map<Network.Service, Set<Network.Provider>> vpcOffSvcProvidersMap = vpcMgr.getVpcOffSvcProvidersMap(vpcRouterDeploymentDefinition.getVpc().getVpcOfferingId());
  
          boolean vpcIsStaticNatProvider = vpcOffSvcProvidersMap.get(Network.Service.StaticNat) != null &&
                  vpcOffSvcProvidersMap.get(Network.Service.StaticNat).contains(Network.Provider.VPCVirtualRouter);
  
          final ServiceOfferingVO routerOffering = _serviceOfferingDao.findById(vpcRouterDeploymentDefinition.getServiceOfferingId());
  
          for (final IPAddressVO ip : ips) {
              if (vpcIsStaticNatProvider || !ip.isOneToOneNat()) {
                  final PublicIp publicIp = PublicIp.createFromAddrAndVlan(ip, _vlanDao.findById(ip.getVlanId()));
                  if ((ip.getState() == IpAddress.State.Allocated  || ip.getState() == IpAddress.State.Allocating)
                          && vpcMgr.isIpAllocatedToVpc(ip)
                          && Objects.nonNull(publicIp.getVlanTag())
                          && !publicVlans.contains(publicIp.getVlanTag())) {
                      logger.debug("Allocating nic for router in vlan " + publicIp.getVlanTag());
                      final NicProfile publicNic = new NicProfile();
                      publicNic.setDefaultNic(false);
                      publicNic.setIPv4Address(publicIp.getAddress()
                                                       .addr());
                      publicNic.setIPv4Gateway(publicIp.getGateway());
                      publicNic.setIPv4Netmask(publicIp.getNetmask());
                      publicNic.setMacAddress(publicIp.getMacAddress());
                      publicNic.setBroadcastType(BroadcastDomainType.Vlan);
                      publicNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(publicIp.getVlanTag()));
                      publicNic.setIsolationUri(IsolationType.Vlan.toUri(publicIp.getVlanTag()));
                      final NetworkOffering publicOffering = _networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemPublicNetwork)
                                                                          .get(0);
                      if (publicNetwork == null) {
                          final List<? extends Network> publicNetworks = _networkMgr.setupNetwork(s_systemAccount, publicOffering, vpcRouterDeploymentDefinition.getPlan(), null, null, false);
                          publicNetwork = publicNetworks.get(0);
                      }
                      publicNics.add(publicNic);
                      publicVlans.add(publicIp.getVlanTag());
                  }
              }
          }
          if (publicNetwork != null) {
              if (networks.get(publicNetwork) != null) {
                  @SuppressWarnings("unchecked")
                  final List<NicProfile> publicNicProfiles = (List<NicProfile>)networks.get(publicNetwork);
                  publicNicProfiles.addAll(publicNics);
                  networks.put(publicNetwork, publicNicProfiles);
              } else {
                  networks.put(publicNetwork, publicNics);
              }
          }
  
          _itMgr.allocate(router.getInstanceName(), template, routerOffering, networks, vpcRouterDeploymentDefinition.getPlan(), hType, null, null);
      }
  
      @Override
      public LinkedHashMap<Network, List<? extends NicProfile>> configureDefaultNics(final RouterDeploymentDefinition routerDeploymentDefinition) throws ConcurrentOperationException, InsufficientAddressCapacityException {
  
          final LinkedHashMap<Network, List<? extends NicProfile>> networks = new LinkedHashMap<Network, List<? extends NicProfile>>(3);
  
          // 1) Control network
          final LinkedHashMap<Network, List<? extends NicProfile>> controlNic = configureControlNic(routerDeploymentDefinition);
          networks.putAll(controlNic);
  
          // 2) Public network
          final LinkedHashMap<Network, List<? extends NicProfile>> publicNic = configurePublicNic(routerDeploymentDefinition, false);
          networks.putAll(publicNic);
  
          // 3) Guest Network
          final LinkedHashMap<Network, List<? extends NicProfile>> guestNic = configureGuestNic(routerDeploymentDefinition);
          networks.putAll(guestNic);
  
          return networks;
      }
  }
  