# Copyright 2019-2020 VMware, Inc.
# All rights reserved. -- VMware Confidential

"""Functions related to VMFS datastores on ESXi host.
"""
import os

from systemStorage import FS_TYPE_VMFS, SYSTEM_DISK_SIZE_SMALL_MB, MiB, IS_ESX

if IS_ESX:
   from systemStorage.esxfs import FssVolume, getFssVolumes, getVolumeFullPath


class DatastoreExtent(FssVolume):
   """Read-only class to query information about a datastore extent (partition).
   """

   def __init__(self, vsiInfo, partInfo):
      super().__init__(vsiInfo)
      self._partInfo = partInfo

      # vsiInfo is not available if the datastore was not mounted because it's
      # a snapshot or missing metadata (such as being unformatted).
      if vsiInfo is not None:
         assert self.partNum == partInfo.num, ("%s: partition number mismatch "
                                               "(FssVolume.partNum=%u != "
                                               "Partition.num=%u)"
                                               % (self.name, self.partNum,
                                                partInfo.num))

   @classmethod
   def fromDisk(cls, disk):
      """Instantiate a DatastoreExtent object from an EsxDisk object.
      """
      partInfo = disk.getDatastore()
      if partInfo is None:
         raise RuntimeError('%s: no datastore on disk' % disk.name)

      volumes = getFssVolumes(diskName=disk.name, fsType=FS_TYPE_VMFS)
      numVolumes = len(volumes)

      # For unformatted partitions or unmounted snapshots, there isn't any
      # volume info. Only error if there is more than 1 VMFS.
      assert numVolumes <= 1, ("%s: %u datastores found on disk (maximum "
                               "allowed is 1)" % (disk.name, numVolumes))

      if numVolumes == 0:
         volumes = [FssVolume(None)]
      return cls.fromFssVolume(volumes[0], partInfo)

   @property
   def startSector(self):
      """Start sector of the partition on the disk.
      """
      return self._partInfo.start

   @property
   def endSector(self):
      """End sector of the partition on the disk.
      """
      return self._partInfo.end

   @property
   def vmfsVersionStr(self):
      """VMFS version string, e.g. 'vmfs6'.
      """
      try:
         return 'vmfs%u' % self.majorVersion
      except Exception:
         return 'invalid'


def canKeepDiskDatastore(disk):
   """True if the datastore on disk (EsxDisk object) can be kept on upgrade.

   ESX requires at least 4GB for bootbanks and OSdata, which is also the minimum
   disk size requirement.
   """
   datastore = disk.getDatastore()
   if datastore is None:
      raise RuntimeError('%s: no datastore on disk' % disk.name)

   return disk.sectorSize * datastore.start >= SYSTEM_DISK_SIZE_SMALL_MB * MiB

def getNextDatastoreLabel():
   """Get the next available datastore label.
   """
   MAX_DATASTORE_NUM = 9999 # some unrealistically high value

   # Counting from datastore1.
   for i in range(1, MAX_DATASTORE_NUM):
      label = 'datastore%u' % i
      path = getVolumeFullPath(label)
      if not os.path.exists(path):
         return label
   raise OSError("failed to find new datastore name")
