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.amazonaws.util.CollectionUtils;
  import com.cloud.dc.DataCenterVO;
  import com.cloud.dc.dao.DataCenterDao;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.host.DetailVO;
  import com.cloud.host.Host;
  import com.cloud.host.dao.HostDetailsDao;
  import com.cloud.network.Network;
  import com.cloud.network.Networks;
  import com.cloud.network.nsx.NsxProvider;
  import com.cloud.network.dao.NetworkDao;
  import com.cloud.network.dao.NsxProviderDao;
  import com.cloud.network.dao.PhysicalNetworkDao;
  import com.cloud.network.dao.PhysicalNetworkVO;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.element.NsxProviderVO;
  import com.cloud.resource.ResourceManager;
  import com.cloud.utils.db.Transaction;
  import com.cloud.utils.db.TransactionCallback;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.google.common.annotations.VisibleForTesting;
  import org.apache.cloudstack.api.command.DeleteNsxControllerCmd;
  import org.apache.cloudstack.api.command.ListNsxControllersCmd;
  import org.apache.cloudstack.api.BaseResponse;
  import org.apache.cloudstack.api.command.AddNsxControllerCmd;
  import org.apache.cloudstack.api.response.NsxControllerResponse;
  import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
  import org.apache.cloudstack.resource.NsxResource;
  import org.apache.commons.lang3.StringUtils;
  
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Objects;
  import java.util.UUID;
  
  public class NsxProviderServiceImpl implements NsxProviderService {
  
      @Inject
      NsxProviderDao nsxProviderDao;
      @Inject
      DataCenterDao dataCenterDao;
      @Inject
      PhysicalNetworkDao physicalNetworkDao;
      @Inject
      NetworkDao networkDao;
      @Inject
      ResourceManager resourceManager;
      @Inject
      HostDetailsDao hostDetailsDao;
  
      @Override
      public NsxProvider addProvider(AddNsxControllerCmd cmd) {
          final Long zoneId = cmd.getZoneId();
          final String name = cmd.getName();
          final String hostname = cmd.getHostname();
          final String port = cmd.getPort() == null || cmd.getPort().equals(StringUtils.EMPTY) ? "443" : cmd.getPort();
          final String username = cmd.getUsername();
          final String password = cmd.getPassword();
          final String tier0Gateway = cmd.getTier0Gateway();
          final String edgeCluster = cmd.getEdgeCluster();
          final String transportZone = cmd.getTransportZone();
  
          Map<String, String> params = new HashMap<>();
          params.put("guid", UUID.randomUUID().toString());
          params.put("zoneId", zoneId.toString());
          params.put("name", name);
          params.put("hostname", hostname);
          params.put("port", port);
          params.put("username", username);
          params.put("password", password);
          params.put("tier0Gateway", tier0Gateway);
          params.put("edgeCluster", edgeCluster);
          params.put("transportZone", transportZone);
  
          Map<String, Object> hostdetails = new HashMap<>(params);
          NsxProvider nsxProvider;
  
          NsxResource nsxResource = new NsxResource();
          try {
              nsxResource.configure(hostname, hostdetails);
              final Host host = resourceManager.addHost(zoneId, nsxResource, nsxResource.getType(), params);
              if (host != null) {
                   nsxProvider = Transaction.execute((TransactionCallback<NsxProviderVO>) status -> {
                      NsxProviderVO nsxProviderVO = new NsxProviderVO.Builder()
                              .setZoneId(zoneId)
                              .setHostId(host.getId())
                              .setProviderName(name)
                              .setHostname(hostname)
                              .setPort(port)
                              .setUsername(username)
                              .setPassword(password)
                              .setTier0Gateway(tier0Gateway)
                              .setEdgeCluster(edgeCluster)
                              .setTransportZone(transportZone)
                              .build();
  
                      nsxProviderDao.persist(nsxProviderVO);
  
                      DetailVO detail = new DetailVO(host.getId(), "nsxcontrollerid",
                              String.valueOf(nsxProviderVO.getId()));
                      hostDetailsDao.persist(detail);
  
                      return nsxProviderVO;
                  });
              } else {
                  throw new CloudRuntimeException("Failed to add NSX controller due to internal error.");
              }
          } catch (ConfigurationException e) {
              throw new CloudRuntimeException(e.getMessage());
          }
          return  nsxProvider;
      }
  
      @Override
      public NsxControllerResponse createNsxControllerResponse(NsxProvider nsxProvider) {
          DataCenterVO zone  = dataCenterDao.findById(nsxProvider.getZoneId());
          if (Objects.isNull(zone)) {
              throw new CloudRuntimeException(String.format("Failed to find zone with id %s", nsxProvider.getZoneId()));
          }
          NsxControllerResponse response = new NsxControllerResponse();
          response.setName(nsxProvider.getProviderName());
          response.setUuid(nsxProvider.getUuid());
          response.setHostname(nsxProvider.getHostname());
          response.setPort(nsxProvider.getPort());
          response.setZoneId(zone.getUuid());
          response.setZoneName(zone.getName());
          response.setTier0Gateway(nsxProvider.getTier0Gateway());
          response.setEdgeCluster(nsxProvider.getEdgeCluster());
          response.setTransportZone(nsxProvider.getTransportZone());
          response.setObjectName("nsxController");
          return response;
      }
  
      @Override
      public List<BaseResponse> listNsxProviders(Long zoneId) {
          List<BaseResponse> nsxControllersResponseList = new ArrayList<>();
          if (zoneId != null) {
              NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId);
              if (Objects.nonNull(nsxProviderVO)) {
                  nsxControllersResponseList.add(createNsxControllerResponse(nsxProviderVO));
              }
          } else {
              List<NsxProviderVO> nsxProviderVOList = nsxProviderDao.listAll();
              for (NsxProviderVO nsxProviderVO : nsxProviderVOList) {
                  nsxControllersResponseList.add(createNsxControllerResponse(nsxProviderVO));
              }
          }
  
          return nsxControllersResponseList;
      }
  
      @Override
      public boolean deleteNsxController(Long nsxControllerId) {
          NsxProviderVO nsxProvider = nsxProviderDao.findById(nsxControllerId);
          if (Objects.isNull(nsxProvider)) {
              throw new InvalidParameterValueException(String.format("Failed to find NSX controller with id: %s", nsxControllerId));
          }
          Long zoneId = nsxProvider.getZoneId();
          // Find the physical network we work for
          List<PhysicalNetworkVO> physicalNetworks = physicalNetworkDao.listByZone(zoneId);
          for (PhysicalNetworkVO physicalNetwork : physicalNetworks) {
              List<NetworkVO> networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId());
              if (!CollectionUtils.isNullOrEmpty(networkList)) {
                  validateNetworkState(networkList);
              }
          }
          nsxProviderDao.remove(nsxControllerId);
          return true;
      }
  
      @Override
      public List<Class<?>> getCommands() {
          List<Class<?>> cmdList = new ArrayList<>();
          if (Boolean.TRUE.equals(NetworkOrchestrationService.NSX_ENABLED.value())) {
              cmdList.add(AddNsxControllerCmd.class);
              cmdList.add(ListNsxControllersCmd.class);
              cmdList.add(DeleteNsxControllerCmd.class);
          }
          return cmdList;
      }
  
      @VisibleForTesting
      void validateNetworkState(List<NetworkVO> networkList) {
          for (NetworkVO network : networkList) {
              if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.NSX &&
                  ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy))) {
                      throw new CloudRuntimeException("This NSX Controller cannot be deleted as there are one or more logical networks provisioned by CloudStack on it.");
              }
          }
      }
  }
  