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.host;
  
  import java.util.Arrays;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.UUID;
  
  import javax.persistence.Column;
  import javax.persistence.Convert;
  import javax.persistence.DiscriminatorColumn;
  import javax.persistence.DiscriminatorType;
  import javax.persistence.Entity;
  import javax.persistence.EnumType;
  import javax.persistence.Enumerated;
  import javax.persistence.GeneratedValue;
  import javax.persistence.GenerationType;
  import javax.persistence.Id;
  import javax.persistence.Inheritance;
  import javax.persistence.InheritanceType;
  import javax.persistence.Table;
  import javax.persistence.Temporal;
  import javax.persistence.TemporalType;
  import javax.persistence.Transient;
  
  import com.cloud.cpu.CPU;
  import org.apache.cloudstack.util.CPUArchConverter;
  import org.apache.cloudstack.util.HypervisorTypeConverter;
  import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
  import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang.BooleanUtils;
  import org.apache.commons.lang3.StringUtils;
  
  import com.cloud.agent.api.VgpuTypesInfo;
  import com.cloud.host.dao.HostTagsDao;
  import com.cloud.hypervisor.Hypervisor.HypervisorType;
  import com.cloud.offering.ServiceOffering;
  import com.cloud.resource.ResourceState;
  import com.cloud.storage.Storage.StoragePoolType;
  import com.cloud.template.VirtualMachineTemplate;
  import com.cloud.util.StoragePoolTypeConverter;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.db.GenericDao;
  
  @Entity
  @Table(name = "host")
  @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
  @DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING, length = 32)
  public class HostVO implements Host {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "id")
      private long id;
  
      @Column(name = "disconnected")
      @Temporal(value = TemporalType.TIMESTAMP)
      private Date disconnectedOn;
  
      @Column(name = "name", nullable = false)
      private String name = null;
  
      /**
       * Note: There is no setter for status because it has to be set in the dao code.
       */
      @Column(name = "status", nullable = false)
      private Status status = null;
  
      @Column(name = "type", updatable = true, nullable = false)
      @Enumerated(value = EnumType.STRING)
      private Type type;
  
      @Column(name = "private_ip_address", nullable = false)
      private String privateIpAddress;
  
      @Column(name = "private_mac_address", nullable = false)
      private String privateMacAddress;
  
      @Column(name = "private_netmask", nullable = false)
      private String privateNetmask;
  
      @Column(name = "public_netmask")
      private String publicNetmask;
  
      @Column(name = "public_ip_address")
      private String publicIpAddress;
  
      @Column(name = "public_mac_address")
      private String publicMacAddress;
  
      @Column(name = "storage_ip_address")
      private String storageIpAddress;
  
      @Column(name = "cluster_id")
      private Long clusterId;
  
      @Column(name = "storage_netmask")
      private String storageNetmask;
  
      @Column(name = "storage_mac_address")
      private String storageMacAddress;
  
      @Column(name = "storage_ip_address_2")
      private String storageIpAddressDeux;
  
      @Column(name = "storage_netmask_2")
      private String storageNetmaskDeux;
  
      @Column(name = "storage_mac_address_2")
      private String storageMacAddressDeux;
  
      @Column(name = "hypervisor_type", updatable = true, nullable = false)
      @Convert(converter = HypervisorTypeConverter.class)
      private HypervisorType hypervisorType;
  
      @Column(name = "proxy_port")
      private Integer proxyPort;
  
      @Column(name = "resource")
      private String resource;
  
      @Column(name = "fs_type")
      @Convert(converter = StoragePoolTypeConverter.class)
      private StoragePoolType fsType;
  
      @Column(name = "available")
      private boolean available = true;
  
      @Column(name = "setup")
      private boolean setup = false;
  
      @Column(name = "resource_state", nullable = false)
      @Enumerated(value = EnumType.STRING)
      private ResourceState resourceState;
  
      @Column(name = "hypervisor_version")
      private String hypervisorVersion;
  
      @Column(name = "arch")
      @Convert(converter = CPUArchConverter.class)
      private CPU.CPUArch arch;
  
      @Column(name = "update_count", updatable = true, nullable = false)
      protected long updated;    // This field should be updated everytime the state is updated.  There's no set method in the vo object because it is done with in the dao code.
  
      @Column(name = "uuid")
      private String uuid;
  
      @Column(name = "storage_access_groups")
      String storageAccessGroups;
  
      // This is a delayed load value.  If the value is null,
      // then this field has not been loaded yet.
      // Call host dao to load it.
      @Transient
      Map<String, String> details;
  
      // This is a delayed load value.  If the value is null,
      // then this field has not been loaded yet.
      // Call host dao to load it.
      @Transient
      List<String> hostTags;
  
      /**
       * This is a delayed load value.
       * If the value is null, then this field has not been loaded yet.
       * Call host dao to load it.
       */
      @Transient
      Boolean isTagARule;
  
      // This value is only for saving and current cannot be loaded.
      @Transient
      HashMap<String, HashMap<String, VgpuTypesInfo>> groupDetails = new HashMap<String, HashMap<String, VgpuTypesInfo>>();
  
      @Override
      public String getStorageIpAddressDeux() {
          return storageIpAddressDeux;
      }
  
      public void setStorageIpAddressDeux(String deuxStorageIpAddress) {
          this.storageIpAddressDeux = deuxStorageIpAddress;
      }
  
      @Override
      public String getStorageNetmaskDeux() {
          return storageNetmaskDeux;
      }
  
      @Override
      public Long getClusterId() {
          return clusterId;
      }
  
      @Override
      public ResourceType resourceType() {
          return ResourceType.Host;
      }
  
      public void setClusterId(Long clusterId) {
          this.clusterId = clusterId;
      }
  
      public void setStorageNetmaskDeux(String deuxStorageNetmask) {
          this.storageNetmaskDeux = deuxStorageNetmask;
      }
  
      @Override
      public String getStorageMacAddressDeux() {
          return storageMacAddressDeux;
      }
  
      public void setStorageMacAddressDeux(String duexStorageMacAddress) {
          this.storageMacAddressDeux = duexStorageMacAddress;
      }
  
      @Override
      public String getPrivateMacAddress() {
          return privateMacAddress;
      }
  
      public void setPrivateMacAddress(String privateMacAddress) {
          this.privateMacAddress = privateMacAddress;
      }
  
      public boolean isAvailable() {
          return available;
      }
  
      public void setAvailable(boolean available) {
          this.available = available;
      }
  
      @Override
      public String getPrivateNetmask() {
          return privateNetmask;
      }
  
      public void setPrivateNetmask(String privateNetmask) {
          this.privateNetmask = privateNetmask;
      }
  
      @Override
      public String getPublicNetmask() {
          return publicNetmask;
      }
  
      public void setPublicNetmask(String publicNetmask) {
          this.publicNetmask = publicNetmask;
      }
  
      @Override
      public String getPublicIpAddress() {
          return publicIpAddress;
      }
  
      public void setPublicIpAddress(String publicIpAddress) {
          this.publicIpAddress = publicIpAddress;
      }
  
      @Override
      public String getPublicMacAddress() {
          return publicMacAddress;
      }
  
      public void setPublicMacAddress(String publicMacAddress) {
          this.publicMacAddress = publicMacAddress;
      }
  
      @Override
      public String getStorageIpAddress() {
          return storageIpAddress;
      }
  
      public void setStorageIpAddress(String storageIpAddress) {
          this.storageIpAddress = storageIpAddress;
      }
  
      @Override
      public String getStorageNetmask() {
          return storageNetmask;
      }
  
      public void setStorageNetmask(String storageNetmask) {
          this.storageNetmask = storageNetmask;
      }
  
      @Override
      public String getStorageMacAddress() {
          return storageMacAddress;
      }
  
      public boolean isSetup() {
          return setup;
      }
  
      public void setSetup(boolean setup) {
          this.setup = setup;
      }
  
      public void setStorageMacAddress(String storageMacAddress) {
          this.storageMacAddress = storageMacAddress;
      }
  
      public String getResource() {
          return resource;
      }
  
      public void setResource(String resource) {
          this.resource = resource;
      }
  
      public Map<String, String> getDetails() {
          return details;
      }
  
      public String getDetail(String name) {
          return details != null ? details.get(name) : null;
      }
  
      public void setDetail(String name, String value) {
          assert (details != null) : "Did you forget to load the details?";
  
          details.put(name, value);
      }
  
      public void setDetails(Map<String, String> details) {
          this.details = details;
      }
  
      public List<String> getHostTags() {
          return hostTags;
      }
  
      public void setHostTags(List<String> hostTags, Boolean isTagARule) {
          this.hostTags = hostTags;
          this.isTagARule = isTagARule;
      }
  
      public Boolean getIsTagARule() {
          return isTagARule;
      }
  
      @Override
      public String getStorageAccessGroups() {
          return storageAccessGroups;
      }
  
      public void setStorageAccessGroups(String storageAccessGroups) {
          this.storageAccessGroups = storageAccessGroups;
      }
  
      public  HashMap<String, HashMap<String, VgpuTypesInfo>> getGpuGroupDetails() {
          return groupDetails;
      }
  
      public void setGpuGroups(HashMap<String, HashMap<String, VgpuTypesInfo>> groupDetails) {
          this.groupDetails = groupDetails;
      }
  
      @Column(name = "data_center_id", nullable = false)
      private long dataCenterId;
  
      @Column(name = "pod_id")
      private Long podId;
  
      @Column(name = "cpu_sockets")
      private Integer cpuSockets;
  
      @Column(name = "cpus")
      private Integer cpus;
  
      @Column(name = "url")
      private String storageUrl;
  
      @Column(name = "speed")
      private Long speed;
  
      @Column(name = "ram")
      private long totalMemory;
  
      @Column(name = "parent", nullable = false)
      private String parent;
  
      @Column(name = "guid", updatable = true, nullable = false)
      private String guid;
  
      @Column(name = "capabilities")
      private String caps;
  
      @Column(name = "total_size")
      private Long totalSize;
  
      @Column(name = "last_ping")
      private long lastPinged;
  
      @Column(name = "mgmt_server_id")
      private Long managementServerId;
  
      @Column(name = "last_mgmt_server_id")
      private Long lastManagementServerId;
  
      @Column(name = "dom0_memory")
      private long dom0MinMemory;
  
      @Column(name = "version")
      private String version;
  
      @Column(name = GenericDao.CREATED_COLUMN)
      private Date created;
  
      @Column(name = GenericDao.REMOVED_COLUMN)
      private Date removed;
  
      public HostVO(String guid) {
          this.guid = guid;
          this.status = Status.Creating;
          this.totalMemory = 0;
          this.dom0MinMemory = 0;
          this.resourceState = ResourceState.Creating;
          this.uuid = UUID.randomUUID().toString();
      }
  
      protected HostVO() {
          this.uuid = UUID.randomUUID().toString();
      }
  
      public HostVO(long id, String name, Type type, String privateIpAddress, String privateNetmask, String privateMacAddress, String publicIpAddress,
              String publicNetmask, String publicMacAddress, String storageIpAddress, String storageNetmask, String storageMacAddress, String deuxStorageIpAddress,
              String duxStorageNetmask, String deuxStorageMacAddress, String guid, Status status, String version, String iqn, Date disconnectedOn, long dcId, Long podId,
              long serverId, long ping, String parent, long totalSize, StoragePoolType fsType) {
          this(id,
              name,
              type,
              privateIpAddress,
              privateNetmask,
              privateMacAddress,
              publicIpAddress,
              publicNetmask,
              publicMacAddress,
              storageIpAddress,
              storageNetmask,
              storageMacAddress,
              guid,
              status,
              version,
              iqn,
              disconnectedOn,
              dcId,
              podId,
              serverId,
              ping,
              null,
              null,
              null,
              0,
              null);
          this.parent = parent;
          this.totalSize = totalSize;
          this.fsType = fsType;
          this.uuid = UUID.randomUUID().toString();
      }
  
      public HostVO(long id, String name, Type type, String privateIpAddress, String privateNetmask, String privateMacAddress, String publicIpAddress,
              String publicNetmask, String publicMacAddress, String storageIpAddress, String storageNetmask, String storageMacAddress, String guid, Status status,
              String version, String url, Date disconnectedOn, long dcId, Long podId, long serverId, long ping, Integer cpus, Long speed, Long totalMemory,
              long dom0MinMemory, String caps) {
          this.id = id;
          this.name = name;
          this.status = status;
          this.type = type;
          this.privateIpAddress = privateIpAddress;
          this.privateNetmask = privateNetmask;
          this.privateMacAddress = privateMacAddress;
          this.publicIpAddress = publicIpAddress;
          this.publicNetmask = publicNetmask;
          this.publicMacAddress = publicMacAddress;
          this.storageIpAddress = storageIpAddress;
          this.storageNetmask = storageNetmask;
          this.storageMacAddress = storageMacAddress;
          this.dataCenterId = dcId;
          this.podId = podId;
          this.cpus = cpus;
          this.version = version;
          this.speed = speed;
          this.totalMemory = totalMemory != null ? totalMemory : 0;
          this.guid = guid;
          this.parent = null;
          this.totalSize = null;
          this.fsType = null;
          this.managementServerId = serverId;
          this.lastPinged = ping;
          this.caps = caps;
          this.disconnectedOn = disconnectedOn;
          this.dom0MinMemory = dom0MinMemory;
          this.storageUrl = url;
          this.uuid = UUID.randomUUID().toString();
      }
  
      public void setPodId(Long podId) {
  
          this.podId = podId;
      }
  
      public void setDataCenterId(long dcId) {
          this.dataCenterId = dcId;
      }
  
      public void setVersion(String version) {
          this.version = version;
      }
  
      public void setStorageUrl(String url) {
          this.storageUrl = url;
      }
  
      public void setDisconnectedOn(Date disconnectedOn) {
          this.disconnectedOn = disconnectedOn;
      }
  
      public String getStorageUrl() {
          return storageUrl;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public void setPrivateIpAddress(String ipAddress) {
          this.privateIpAddress = ipAddress;
      }
  
      public void setCpuSockets(Integer cpuSockets) {
          this.cpuSockets = cpuSockets;
      }
  
      public void setCpus(Integer cpus) {
          this.cpus = cpus;
      }
  
      public void setSpeed(Long speed) {
          this.speed = speed;
      }
  
      public void setTotalMemory(long totalMemory) {
          this.totalMemory = totalMemory;
      }
  
      public void setParent(String parent) {
          this.parent = parent;
      }
  
      public void setCaps(String caps) {
          this.caps = caps;
      }
  
      public void setTotalSize(Long totalSize) {
          this.totalSize = totalSize;
      }
  
      public void setLastPinged(long lastPinged) {
          this.lastPinged = lastPinged;
      }
  
      public void setManagementServerId(Long managementServerId) {
          this.managementServerId = managementServerId;
      }
  
      public void setLastManagementServerId(Long lastManagementServerId) {
          this.lastManagementServerId = lastManagementServerId;
      }
  
      @Override
      public long getLastPinged() {
          return lastPinged;
      }
  
      @Override
      public String getParent() {
          return parent;
      }
  
      @Override
      public long getTotalSize() {
          return totalSize;
      }
  
      @Override
      public String getCapabilities() {
          return caps;
      }
  
      @Override
      public Date getCreated() {
          return created;
      }
  
      @Override
      public Date getRemoved() {
          return removed;
      }
  
      @Override
      public String getVersion() {
          return version;
      }
  
      public void setType(Type type) {
          this.type = type;
      }
  
      @Override
      public long getId() {
          return id;
      }
  
      @Override
      public String getName() {
          return name;
      }
  
      @Override
      public Status getStatus() {
          return status;
      }
  
      @Override
      public long getDataCenterId() {
          return dataCenterId;
      }
  
      @Override
      public Long getPodId() {
          return podId;
      }
  
      @Override
      public Long getManagementServerId() {
          return managementServerId;
      }
  
      @Override
      public Long getLastManagementServerId() {
          return lastManagementServerId;
      }
  
      @Override
      public Date getDisconnectedOn() {
          return disconnectedOn;
      }
  
      @Override
      public String getPrivateIpAddress() {
          return privateIpAddress;
      }
  
      @Override
      public String getGuid() {
          return guid;
      }
  
      public void setGuid(String guid) {
          this.guid = guid;
      }
  
      @Override
      public Integer getCpuSockets() {
          return cpuSockets;
      }
  
      @Override
      public Integer getCpus() {
          return cpus;
      }
  
      @Override
      public Long getSpeed() {
          return speed;
      }
  
      @Override
      public Long getTotalMemory() {
          return totalMemory;
      }
  
      @Override
      public Integer getProxyPort() {
          return proxyPort;
      }
  
      public void setProxyPort(Integer port) {
          proxyPort = port;
      }
  
      public StoragePoolType getFsType() {
          return fsType;
      }
  
      @Override
      public Type getType() {
          return type;
      }
  
      @Override
      public int hashCode() {
          return NumbersUtil.hash(id);
      }
  
      @Override
      public boolean equals(Object obj) {
          if (obj instanceof HostVO) {
              return ((HostVO)obj).getId() == this.getId();
          } else {
              return false;
          }
      }
  
      @Override
      public String toString() {
          return String.format("Host %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "uuid", "name", "type"));
      }
  
      public void setHypervisorType(HypervisorType hypervisorType) {
          this.hypervisorType = hypervisorType;
      }
  
      @Override
      public HypervisorType getHypervisorType() {
          return hypervisorType;
      }
  
      public void setHypervisorVersion(String hypervisorVersion) {
          this.hypervisorVersion = hypervisorVersion;
      }
  
      @Override
      public String getHypervisorVersion() {
          return hypervisorVersion;
      }
  
      @Override
      @Transient
      public Status getState() {
          return status;
      }
  
      @Override
      public ResourceState getResourceState() {
          return resourceState;
      }
  
      @Override
      public CPU.CPUArch getArch() {
          return arch;
      }
  
      public void setArch(CPU.CPUArch arch) {
          this.arch = arch;
      }
  
      public void setResourceState(ResourceState state) {
          resourceState = state;
      }
  
      @Override
      public boolean isInMaintenanceStates() {
          return ResourceState.isMaintenanceState(getResourceState());
      }
      @Override
      public boolean isDisabled() {
          return (getResourceState() == ResourceState.Disabled);
      }
  
      public long getUpdated() {
          return updated;
      }
  
      public long incrUpdated() {
          updated++;
          return updated;
      }
  
      @Override
      public String getUuid() {
          return this.uuid;
      }
  
      public void setUuid(String uuid) {
          this.uuid = uuid;
      }
  
      private Set<String> getHostServiceOfferingAndTemplateStrictTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
          if (StringUtils.isEmpty(serviceOffering.getHostTag()) && StringUtils.isEmpty(template.getTemplateTag())) {
              return new HashSet<>();
          }
          List<String> hostTagsList = getHostTags();
          HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
          HashSet<String> tags = new HashSet<>();
          if (StringUtils.isNotEmpty(serviceOffering.getHostTag())) {
              tags.addAll(Arrays.asList(serviceOffering.getHostTag().split(",")));
          }
          if (StringUtils.isNotEmpty(template.getTemplateTag())) {
              tags.add(template.getTemplateTag());
          }
          tags.removeIf(tag -> !strictHostTags.contains(tag));
          tags.removeAll(hostTagsSet);
          return tags;
      }
  
      public boolean checkHostServiceOfferingAndTemplateTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
          if (serviceOffering == null || template == null) {
              return false;
          }
          Set<String> tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags);
          if (tags.isEmpty()) {
              return true;
          }
          List<String> hostTagsList = getHostTags();
          HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
          return hostTagsSet.containsAll(tags);
      }
  
      public Set<String> getHostServiceOfferingAndTemplateMissingTags(ServiceOffering serviceOffering, VirtualMachineTemplate template, Set<String> strictHostTags) {
          Set<String> tags = getHostServiceOfferingAndTemplateStrictTags(serviceOffering, template, strictHostTags);
          if (tags.isEmpty()) {
              return new HashSet<>();
          }
          List<String> hostTagsList = getHostTags();
          HashSet<String> hostTagsSet = CollectionUtils.isNotEmpty(hostTagsList) ? new HashSet<>(hostTagsList) : new HashSet<>();
          tags.removeAll(hostTagsSet);
          return tags;
      }
  
      public boolean checkHostServiceOfferingTags(ServiceOffering serviceOffering) {
          if (serviceOffering == null) {
              return false;
          }
  
          if (BooleanUtils.isTrue(this.getIsTagARule())) {
              return TagAsRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value());
          }
  
          if (StringUtils.isEmpty(serviceOffering.getHostTag())) {
              return true;
          }
          List<String> serviceOfferingTags = Arrays.asList(serviceOffering.getHostTag().split(","));
          return this.getHostTags() != null && this.getHostTags().containsAll(serviceOfferingTags);
      }
  
      @Override
      public PartitionType partitionType() {
          return PartitionType.Host;
      }
  }
  