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.api.query.dao;
  
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import javax.inject.Inject;
  
  import org.apache.cloudstack.annotation.AnnotationService;
  import org.apache.cloudstack.annotation.dao.AnnotationDao;
  import org.apache.cloudstack.api.ApiConstants;
  import org.apache.cloudstack.api.response.ServiceOfferingResponse;
  import org.apache.cloudstack.context.CallContext;
  import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
  import org.apache.cloudstack.vm.lease.VMLeaseManager;
  import org.apache.commons.lang3.StringUtils;
  import org.springframework.stereotype.Component;
  
  import com.cloud.api.ApiDBUtils;
  import com.cloud.api.query.vo.ServiceOfferingJoinVO;
  import com.cloud.dc.VsphereStoragePolicyVO;
  import com.cloud.dc.dao.VsphereStoragePolicyDao;
  import com.cloud.offering.ServiceOffering;
  import com.cloud.server.ResourceTag.ResourceObjectType;
  import com.cloud.storage.DiskOfferingVO;
  import com.cloud.user.AccountManager;
  import com.cloud.utils.db.GenericDaoBase;
  import com.cloud.utils.db.Filter;
  import com.cloud.utils.db.SearchBuilder;
  import com.cloud.utils.db.SearchCriteria;
  import com.cloud.utils.db.TransactionLegacy;
  
  import static org.apache.cloudstack.query.QueryService.SortKeyAscending;
  
  @Component
  public class ServiceOfferingJoinDaoImpl extends GenericDaoBase<ServiceOfferingJoinVO, Long> implements ServiceOfferingJoinDao {
  
      @Inject
      VsphereStoragePolicyDao _vsphereStoragePolicyDao;
      @Inject
      private AnnotationDao annotationDao;
      @Inject
      private ConfigurationDao configDao;
      @Inject
      private AccountManager accountManager;
  
      private SearchBuilder<ServiceOfferingJoinVO> sofIdSearch;
  
      private SearchBuilder<ServiceOfferingJoinVO> srvOfferingSearch;
  
      /**
       * Constant used to convert GB into Bytes (or the other way around).
       * GB   *  MB  *  KB  = Bytes //
       * 1024 * 1024 * 1024 = 1073741824
       */
      private static final long GB_TO_BYTES = 1073741824;
  
  
      private static final String LIST_DOMAINS_OF_SERVICE_OFFERINGS_USED_BY_DOMAIN_PATH = "SELECT sov.domain_id, \n" +
              "            GROUP_CONCAT('Offering:', vm.uuid) \n" +
              "            FROM   cloud.service_offering_view AS sov\n" +
              "            INNER  JOIN cloud.vm_instance AS vm ON (vm.service_offering_id  = sov.id) \n" +
              "            INNER  JOIN cloud.domain AS domain ON (domain.id = vm.domain_id) \n" +
              "            INNER  JOIN cloud.domain AS domain_so ON (domain_so.id = sov.domain_id) \n" +
              "            WHERE  domain.path LIKE ? \n" +
              "            AND    domain_so.path NOT LIKE ? \n " +
              "            AND    vm.removed IS NULL \n" +
              "            GROUP  BY sov.id";
  
      protected ServiceOfferingJoinDaoImpl() {
  
          sofIdSearch = createSearchBuilder();
          sofIdSearch.and("id", sofIdSearch.entity().getId(), SearchCriteria.Op.EQ);
          sofIdSearch.done();
  
          srvOfferingSearch = createSearchBuilder();
          srvOfferingSearch.and("idIN", srvOfferingSearch.entity().getId(), SearchCriteria.Op.IN);
          srvOfferingSearch.done();
  
          this._count = "select count(distinct service_offering_view.id) from service_offering_view WHERE ";
      }
  
      @Override
      public List<ServiceOfferingJoinVO> findByDomainId(long domainId) {
          SearchBuilder<ServiceOfferingJoinVO> sb = createSearchBuilder();
          sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.FIND_IN_SET);
          sb.done();
  
          SearchCriteria<ServiceOfferingJoinVO> sc = sb.create();
          sc.setParameters("domainId", String.valueOf(domainId));
          return listBy(sc);
      }
  
      @Override
      public ServiceOfferingResponse newServiceOfferingResponse(ServiceOfferingJoinVO offering) {
  
          ServiceOfferingResponse offeringResponse = new ServiceOfferingResponse();
          offeringResponse.setId(offering.getUuid());
          offeringResponse.setName(offering.getName());
          offeringResponse.setState(offering.getState().toString());
          offeringResponse.setIsSystemOffering(offering.isSystemUse());
          offeringResponse.setDefaultUse(offering.isDefaultUse());
          offeringResponse.setSystemVmType(offering.getSystemVmType());
          offeringResponse.setDisplayText(offering.getDisplayText());
          offeringResponse.setProvisioningType(offering.getProvisioningType().toString());
          offeringResponse.setCpuNumber(offering.getCpu());
          offeringResponse.setCpuSpeed(offering.getSpeed());
          offeringResponse.setMemory(offering.getRamSize());
          offeringResponse.setCreated(offering.getCreated());
          offeringResponse.setStorageType(offering.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
          offeringResponse.setOfferHa(offering.isOfferHA());
          offeringResponse.setLimitCpuUse(offering.isLimitCpuUse());
          offeringResponse.setVolatileVm(offering.getVolatileVm());
          offeringResponse.setTags(offering.getTags());
          offeringResponse.setDomain(offering.getDomainName());
          offeringResponse.setDomainId(offering.getDomainUuid());
          offeringResponse.setZone(offering.getZoneName());
          offeringResponse.setZoneId(offering.getZoneUuid());
          offeringResponse.setGpuCardId(offering.getGpuCardUuid());
          offeringResponse.setGpuCardName(offering.getGpuCardName());
          offeringResponse.setVgpuProfileId(offering.getVgpuProfileUuid());
          offeringResponse.setVgpuProfileName(offering.getVgpuProfileName());
          offeringResponse.setVideoRam(offering.getVideoRam());
          offeringResponse.setMaxHeads(offering.getMaxHeads());
          offeringResponse.setMaxResolutionX(offering.getMaxResolutionX());
          offeringResponse.setMaxResolutionY(offering.getMaxResolutionY());
          offeringResponse.setGpuCount(offering.getGpuCount());
          offeringResponse.setGpuDisplay(offering.getGpuDisplay());
          offeringResponse.setNetworkRate(offering.getRateMbps());
          offeringResponse.setHostTag(offering.getHostTag());
          offeringResponse.setDeploymentPlanner(offering.getDeploymentPlanner());
          offeringResponse.setCustomizedIops(offering.isCustomizedIops());
          offeringResponse.setMinIops(offering.getMinIops());
          offeringResponse.setMaxIops(offering.getMaxIops());
          offeringResponse.setHypervisorSnapshotReserve(offering.getHypervisorSnapshotReserve());
          offeringResponse.setBytesReadRate(offering.getBytesReadRate());
          offeringResponse.setBytesReadRateMax(offering.getBytesReadRateMax());
          offeringResponse.setBytesReadRateMaxLength(offering.getBytesReadRateMaxLength());
          offeringResponse.setBytesWriteRate(offering.getBytesWriteRate());
          offeringResponse.setBytesWriteRateMax(offering.getBytesWriteRateMax());
          offeringResponse.setBytesWriteRateMaxLength(offering.getBytesWriteRateMaxLength());
          offeringResponse.setIopsReadRate(offering.getIopsReadRate());
          offeringResponse.setIopsReadRateMax(offering.getIopsReadRateMax());
          offeringResponse.setIopsReadRateMaxLength(offering.getIopsReadRateMaxLength());
          offeringResponse.setIopsWriteRate(offering.getIopsWriteRate());
          offeringResponse.setIopsWriteRateMax(offering.getIopsWriteRateMax());
          offeringResponse.setIopsWriteRateMaxLength(offering.getIopsWriteRateMaxLength());
          Map<String, String> offeringDetails = ApiDBUtils.getResourceDetails(offering.getId(), ResourceObjectType.ServiceOffering);
          offeringResponse.setDetails(offeringDetails);
          offeringResponse.setObjectName("serviceoffering");
          offeringResponse.setIscutomized(offering.isDynamic());
          offeringResponse.setCacheMode(offering.getCacheMode());
          offeringResponse.setDynamicScalingEnabled(offering.isDynamicScalingEnabled());
          offeringResponse.setEncryptRoot(offering.getEncryptRoot());
  
          if (offeringDetails != null && !offeringDetails.isEmpty()) {
              String vsphereStoragePolicyId = offeringDetails.get(ApiConstants.STORAGE_POLICY);
              if (vsphereStoragePolicyId != null) {
                  VsphereStoragePolicyVO vsphereStoragePolicyVO = _vsphereStoragePolicyDao.findById(Long.parseLong(vsphereStoragePolicyId));
                  if (vsphereStoragePolicyVO != null)
                      offeringResponse.setVsphereStoragePolicy(vsphereStoragePolicyVO.getName());
              }
              String purgeResource = offeringDetails.get(ServiceOffering.PURGE_DB_ENTITIES_KEY);
              if (StringUtils.isNotBlank(purgeResource)) {
                  offeringResponse.setPurgeResources(Boolean.parseBoolean(purgeResource));
              }
          }
  
          if (VMLeaseManager.InstanceLeaseEnabled.value() && offering.getLeaseDuration() != null && offering.getLeaseDuration() > 0L) {
              offeringResponse.setLeaseDuration(offering.getLeaseDuration());
              offeringResponse.setLeaseExpiryAction(offering.getLeaseExpiryAction().name());
          }
  
          long rootDiskSizeInGb = (long) offering.getRootDiskSize() / GB_TO_BYTES;
          offeringResponse.setRootDiskSize(rootDiskSizeInGb);
          offeringResponse.setDiskOfferingStrictness(offering.getDiskOfferingStrictness());
          DiskOfferingVO diskOfferingVO = ApiDBUtils.findDiskOfferingById(offering.getDiskOfferingId());
          if (diskOfferingVO != null) {
              offeringResponse.setDiskOfferingId(offering.getDiskOfferingUuid());
              offeringResponse.setDiskOfferingName(offering.getDiskOfferingName());
              offeringResponse.setDiskOfferingDisplayText(offering.getDiskOfferingDisplayText());
          }
  
          offeringResponse.setHasAnnotation(annotationDao.hasAnnotations(offering.getUuid(), AnnotationService.EntityType.SERVICE_OFFERING.name(),
                  accountManager.isRootAdmin(CallContext.current().getCallingAccount().getId())));
  
          return offeringResponse;
      }
  
      @Override
      public ServiceOfferingJoinVO newServiceOfferingView(ServiceOffering offering) {
          SearchCriteria<ServiceOfferingJoinVO> sc = sofIdSearch.create();
          sc.setParameters("id", offering.getId());
          List<ServiceOfferingJoinVO> offerings = searchIncludingRemoved(sc, null, null, false);
          assert offerings != null && offerings.size() == 1 : "No service offering found for offering id " + offering.getId();
          return offerings.get(0);
      }
  
      @Override
      public Map<Long, List<String>> listDomainsOfServiceOfferingsUsedByDomainPath(String domainPath) {
          logger.debug("Retrieving the domains of the service offerings used by domain with path [{}].", domainPath);
  
          TransactionLegacy txn = TransactionLegacy.currentTxn();
          try (PreparedStatement pstmt = txn.prepareStatement(LIST_DOMAINS_OF_SERVICE_OFFERINGS_USED_BY_DOMAIN_PATH)) {
              Map<Long, List<String>> domainsOfServiceOfferingsUsedByDomainPath = new HashMap<>();
  
              String domainSearch = domainPath.concat("%");
              pstmt.setString(1, domainSearch);
              pstmt.setString(2, domainSearch);
  
              try (ResultSet rs = pstmt.executeQuery()) {
                  while (rs.next()) {
                      Long domainId = rs.getLong(1);
                      List<String> vmUuids = Arrays.asList(rs.getString(2).split(","));
  
                      domainsOfServiceOfferingsUsedByDomainPath.put(domainId, vmUuids);
                  }
              }
  
              return domainsOfServiceOfferingsUsedByDomainPath;
          } catch (SQLException e) {
              logger.error(String.format("Failed to retrieve the domains of the service offerings used by domain with path [%s] due to [%s]. Returning an empty "
                      + "list of domains.", domainPath, e.getMessage()));
  
              logger.debug(String.format("Failed to retrieve the domains of the service offerings used by domain with path [%s]. Returning an empty "
                      + "list of domains.", domainPath), e);
  
              return new HashMap<>();
          }
      }
  
      @Override
      public List<ServiceOfferingJoinVO> searchByIds(Long... offeringIds) {
          Filter searchFilter = new Filter(ServiceOfferingJoinVO.class, "sortKey", SortKeyAscending.value());
          searchFilter.addOrderBy(ServiceOfferingJoinVO.class, "id", true);
  
          // set detail batch query size
          int DETAILS_BATCH_SIZE = 2000;
          String batchCfg = configDao.getValue("detail.batch.query.size");
          if (batchCfg != null) {
              DETAILS_BATCH_SIZE = Integer.parseInt(batchCfg);
          }
  
          List<ServiceOfferingJoinVO> uvList = new ArrayList<>();
          // query details by batches
          int curr_index = 0;
          if (offeringIds.length > DETAILS_BATCH_SIZE) {
              while ((curr_index + DETAILS_BATCH_SIZE) <= offeringIds.length) {
                  Long[] ids = new Long[DETAILS_BATCH_SIZE];
                  for (int k = 0, j = curr_index; j < curr_index + DETAILS_BATCH_SIZE; j++, k++) {
                      ids[k] = offeringIds[j];
                  }
                  SearchCriteria<ServiceOfferingJoinVO> sc = srvOfferingSearch.create();
                  sc.setParameters("idIN", ids);
                  List<ServiceOfferingJoinVO> offerings = searchIncludingRemoved(sc, searchFilter, null, false);
                  if (offerings != null) {
                      uvList.addAll(offerings);
                  }
                  curr_index += DETAILS_BATCH_SIZE;
              }
          }
          if (curr_index < offeringIds.length) {
              int batch_size = (offeringIds.length - curr_index);
              // set the ids value
              Long[] ids = new Long[batch_size];
              for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
                  ids[k] = offeringIds[j];
              }
              SearchCriteria<ServiceOfferingJoinVO> sc = srvOfferingSearch.create();
              sc.setParameters("idIN", ids);
              List<ServiceOfferingJoinVO> offerings = searchIncludingRemoved(sc, searchFilter, null, false);
              if (offerings != null) {
                  uvList.addAll(offerings);
              }
          }
          return uvList;
      }
  }
  