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.service;
  
  import com.cloud.dc.VlanDetailsVO;
  import com.cloud.deploy.DeploymentPlan;
  import com.cloud.exception.ConcurrentOperationException;
  import com.cloud.exception.InsufficientAddressCapacityException;
  import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
  import com.cloud.network.Network;
  import com.cloud.network.Networks;
  import com.cloud.network.dao.IPAddressVO;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.guru.PublicNetworkGuru;
  import com.cloud.network.netris.NetrisService;
  import com.cloud.network.vpc.VpcOffering;
  import com.cloud.network.vpc.VpcOfferingVO;
  import com.cloud.network.vpc.VpcVO;
  import com.cloud.offering.NetworkOffering;
  import com.cloud.user.Account;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.vm.NicProfile;
  import com.cloud.vm.VirtualMachineProfile;
  import org.apache.cloudstack.api.ApiConstants;
  import org.apache.commons.collections.CollectionUtils;
  
  import javax.inject.Inject;
  import java.util.List;
  import java.util.stream.Collectors;
  
  public class NetrisPublicNetworkGuru extends PublicNetworkGuru {
  
      @Inject
      private NetrisService netrisService;
  
      public NetrisPublicNetworkGuru() {
          super();
      }
  
      @Override
      protected boolean canHandle(NetworkOffering offering) {
          boolean isForNetris = networkModel.isProviderForNetworkOffering(Network.Provider.Netris, offering.getId());
          return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && isForNetris;
      }
  
      @Override
      public Network design(NetworkOffering offering, DeploymentPlan plan, Network network, String name, Long vpcId, Account owner) {
          if (!canHandle(offering)) {
              return null;
          }
  
          if (offering.getTrafficType() == Networks.TrafficType.Public) {
              return new NetworkVO(offering.getTrafficType(), Networks.Mode.Static, network.getBroadcastDomainType(), offering.getId(), Network.State.Setup, plan.getDataCenterId(),
                      plan.getPhysicalNetworkId(), offering.isRedundantRouter());
          }
          return null;
      }
  
      @Override
      public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
          logger.debug("Netris Public network guru: allocate");
  
          IPAddressVO ipAddress = _ipAddressDao.findByIp(nic.getIPv4Address());
          if (ipAddress == null) {
              String err = String.format("Cannot find the IP address %s", nic.getIPv4Address());
              logger.error(err);
              throw new CloudRuntimeException(err);
          }
          Long vpcId = ipAddress.getVpcId();
          boolean isForVpc = vpcId != null;
          VpcVO vpc = vpcDao.findById(vpcId);
          if (vpc == null) {
              String err = String.format("Cannot find a VPC with ID %s", vpcId);
              logger.error(err);
              throw new CloudRuntimeException(err);
          }
  
          // For NSX, use VR Public IP != Source NAT
          List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(vpc.getId(), true);
          if (CollectionUtils.isEmpty(ips)) {
              String err = String.format("Cannot find a source NAT IP for the VPC %s", vpc.getName());
              logger.error(err);
              throw new CloudRuntimeException(err);
          }
  
          ips = ips.stream().filter(x -> !x.getAddress().addr().equals(nic.getIPv4Address())).collect(Collectors.toList());
          // Use Source NAT IP address from the Netris Public Range. Do not Use the VR Public IP address
          ipAddress = ips.get(0);
          if (ipAddress.isSourceNat() && !ipAddress.isForSystemVms()) {
              VlanDetailsVO detail = vlanDetailsDao.findDetail(ipAddress.getVlanId(), ApiConstants.NETRIS_DETAIL_KEY);
              if (detail != null && detail.getValue().equalsIgnoreCase("true")) {
                  long accountId = vpc.getAccountId();
                  long domainId = vpc.getDomainId();
                  long dataCenterId = vpc.getZoneId();
                  long resourceId = vpc.getId();
                  Network.Service[] services = { Network.Service.SourceNat };
                  long networkOfferingId = vpc.getVpcOfferingId();
                  VpcOfferingVO vpcVO = vpcOfferingDao.findById(networkOfferingId);
                  boolean sourceNatEnabled = !NetworkOffering.NetworkMode.ROUTED.equals(vpcVO.getNetworkMode()) &&
                          vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services);
  
                  logger.info(String.format("Creating Netris VPC %s", vpc.getName()));
                  boolean result = netrisService.createVpcResource(dataCenterId, accountId, domainId, resourceId, vpc.getName(), sourceNatEnabled, vpc.getCidr(), true);
                  if (!result) {
                      String msg = String.format("Error creating Netris VPC %s", vpc.getName());
                      logger.error(msg);
                      throw new CloudRuntimeException(msg);
                  }
  
                  boolean hasNatSupport = false;
                  VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId());
                  hasNatSupport = NetworkOffering.NetworkMode.NATTED.equals(vpcOffering.getNetworkMode());
  
                  if (!hasNatSupport) {
                      return nic;
                  }
  
                  String snatIP = ipAddress.getAddress().addr();
                  result = netrisService.createSnatRule(dataCenterId, accountId, domainId, vpc.getName(), vpc.getId(), network.getName(), network.getId(), isForVpc, vpc.getCidr(), snatIP);
                  if (!result) {
                      String msg = String.format("Could not create Netris Nat Rule for IP %s", snatIP);
                      logger.error(msg);
                      throw new CloudRuntimeException(msg);
                  }
  
              }
          }
          return nic;
      }
  }
  