# Copyright (c) 2010-2024 Broadcom. All Rights Reserved.
# Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
# and/or its subsidiaries.

class EsxupdateError(Exception):
   description = ('Abstract base class for esxupdate exceptions.  Each '
                  'exception type has a class variable, errno, which is an '
                  'error code unique to the related error, and description, '
                  'which is a string description of the exception.')
   # Same errno as any non EsxupdateError exception, should be overridden if
   # an inheriting class can be raised in esxupdate/PatchManager.
   errno = 99
   attrs = tuple()

   def __init__(self, *args, **kwargs):
      # 'self' here is likely to be an instance of a child class, hence attrs
      # will actually contain something.
      arglen = len(args) + len(kwargs)
      if arglen > len(self.attrs):
         raise TypeError('%s expected %s arguments, got %s' %
                         (self.__class__, len(self.attrs), arglen))
      alreadyset = list()
      for i in range(len(args)):
         setattr(self, self.attrs[i], args[i])
         alreadyset.append(self.attrs[i])
      for k, v in kwargs.items():
         if k not in self.attrs:
            raise TypeError("%s got an unexpected keyword argument '%s'" %
                            (self.__class__, k))
         if k in alreadyset:
            raise TypeError("%s got multiple values for keyword argument '%s'" %
                            (self.__class__, k))
         setattr(self, k, v)
      # Note:  Built-in Exception() class does not normally accept kw args.
      Exception.__init__(self, *(list(args) + list(kwargs.values())))

   def __str__(self):
      if self.attrs:
         # Print validation issues when problemset is present
         if 'problemset' in self.__dict__.keys() and self.problemset:
            return '\n'.join([str(p) for p in self.problemset])
         else:
            return Exception.__str__(self)
      else:
         return self.description

class NotRootError(EsxupdateError):
   description = 'The current user is not UID 0 (root).'
   errno = 1

class LockingError(EsxupdateError):
   description = 'Another instance of esxupdate is currently running.'
   errno = 3
   attrs = ('msg',)

class MetadataDownloadError(EsxupdateError):
   description = 'Failed to download metadata.'
   errno = 4
   attrs = ('url', 'localfile', 'msg')

class MetadataFormatError(EsxupdateError):
   description = 'The format of the metadata is invalid.'
   errno = 5
   attrs = ('filename', 'msg')

class VibDownloadError(EsxupdateError):
   description = 'Failed to download VIB.'
   errno = 7
   attrs = ('url', 'localfile', 'msg')

class VibFormatError(EsxupdateError):
   description = 'The format of the VIB is invalid.'
   errno = 6
   attrs = ('filename', 'msg')

class VibValidationError(EsxupdateError):
   description = "An error occurred validating VIB contents."
   errno = 8
   attrs = ("filename", "errors", "msg", "problemset",)

class VibXmlSchemaLoadError(EsxupdateError):
   description = "Failed to load xml schema for vib validation."
   attrs = ("filename", "msg")

class VibIOError(EsxupdateError):
   description = 'Error reading or writing VIB data.'
   errno = 9
   attrs = ('filename', 'msg')

class VibHwPlatformError(EsxupdateError):
   description = "VIB vendor/model is not compatible with the host hardware."
   errno = 49
   attrs = ('msg')

class FileIOError(EsxupdateError):
   description = 'Unable to create, write or read a file as expected.'
   errno = 10
   attrs = ('filename', 'msg')

class DatabaseFormatError(EsxupdateError):
   description = 'The format of the database is invalid.'
   errno = 11
   attrs = ('filename', 'msg')

class DatabaseIOError(EsxupdateError):
   description = 'Unable to read or write to database.'
   errno = 12
   attrs = ('filename', 'msg')

class ReleaseUnitIOError(EsxupdateError):
   description = 'Unable to read or write base image or addon JSON file.'
   errno = 41
   attrs = ('filename', 'msg')

class ProfileFormatError(EsxupdateError):
   description = 'The format of the image profile is invalid.'
   errno = 2
   attrs = ('profilename', 'msg')

class ProfileValidationError(EsxupdateError):
   description = 'The image profile could not be validated.'
   errno = 32
   attrs = ('profilename', 'msg', 'problemset',)

class ProfileIOError(EsxupdateError):
   description = 'Unable to read or write profile data.'
   attrs = ('msg',)

class ProfileVibRemoval(EsxupdateError):
   description = 'Profile installation led to the removal of VIBs.'
   attrs = ('profile', 'removed', 'msg')

class ImageIOError(EsxupdateError):
   description = 'An error occurred reading or writing an image.'
   attrs = ('filename', 'msg')

class NoMatchError(EsxupdateError):
   """ Raised due to one of the following:
       1. Specified vib name-version, vendor-name-version, vendor-name,
          does not match any VIBs in metadata or database
       2. Specified image profile name does not match any in metadata
       3. Specified image profile matches more than one profile in metadata
       4. Image Profile contains VIB IDs which are not found in the metadata.
   """
   description = 'No matching VIB or image profile was found.'
   errno = 13
   attrs = ('id', 'msg')

class DependencyError(EsxupdateError):
   description = 'There was an error resolving dependencies.'
   errno = 14
   attrs = ('msg', 'problemset',)

class InstallationError(EsxupdateError):
   description = 'The installation transaction failed.'
   errno = 15
   attrs = ('cause', 'vibs', 'msg')
   def __str__(self):
      """When printing, omit cause, and form a string from VIB IDs when given.
      """
      if self.vibs:
         return "%s: %s" % (', '.join(self.vibs), self.msg)
      else:
         return self.msg

class MaintenanceModeError(EsxupdateError):
   description = 'Maintenance mode is not enabled or could not be determined.'
   errno = 18
   attrs = ('msg',)

class VibSignatureError(EsxupdateError):
   """ Base class for all Vib signature errors """
   pass

class VibSigMissingError(VibSignatureError):
   description = 'Vib signature is missing.'
   errno = 20
   attrs = ('vib', 'msg')

class VibSigFormatError(VibSignatureError):
   description = 'pkcs7.sig file is too short or corrupted.'
   errno = 22
   attrs = ('vib', 'msg')

class VibSigInvalidError(VibSignatureError):
   description = 'PKCS7 certificate is not valid (expired, etc) or ' \
                 'the public key is missing.'
   errno = 23
   attrs = ('vib', 'msg')

class VibCertStoreError(VibSignatureError):
   description = 'Cannot import certificate or revocation list into '\
                 'the certificate store because the certs are invalid '\
                 'or missing.'
   errno = 17
   attrs = ('vib', 'msg')

class VibSigDigestError(VibSignatureError):
   description = 'Vib digest does not match the digest in the PKCS7 signature.'
   errno = 24
   attrs = ('vib', 'msg')

class VibSigningError(VibSignatureError):
   description = 'An error occured signing a VIB.'
   attrs = ('vib', 'msg')

class VibPayloadDigestError(VibSignatureError):
   errno = 19
   description = 'An error occured validaing VIB payload contents.'
   attrs = ('vib', 'payload', 'msg')

class BundleFormatError(EsxupdateError):
   description = 'Invalid bundle ZIP archive, or missing metadata.zip inside.'
   errno = 27
   attrs = ('filename', 'msg')

class BundleIOError(EsxupdateError):
   description = 'Error reading or writing Bundle data.'
   errno = 28
   attrs = ('filename', 'msg')

class LiveInstallationError(InstallationError):
   """Live VIB installation error, a sub-class of InstallationError with
      same attributes and __str__ method.
   """
   description = 'Live vib installation failed. An immediate reboot ' \
                 'is required to clear live vib installation failure.'
   errno = 29

class StatelessError(EsxupdateError):
   description = 'Cannot apply rebootrequired operation on stateless host.'
   errno = 30
   attrs = ('msg',)

class HardwareError(EsxupdateError):
   description = 'Hardware requirement does not meet.'
   errno = 31
   attrs = ('msg',)

class InstallerNotAppropriate(EsxupdateError):
   description = 'Environment is not appropriate for this type of installer.'
   errno = 36
   attrs = ('installer', 'msg',)

class AcceptanceConfigError(EsxupdateError):
   description = 'Failed to change the acceptance level configuration or ' \
                 'there is an issue with current acceptance setting.'
   errno = 33
   attrs = ('msg', 'problemset',)

class ChecksumVerificationError(EsxupdateError):
   description = 'Error in verifying the checksum of a payload.'
   errno = 34
   attrs = ('msg',)

class BulletinValidationError(EsxupdateError):
   description = "An error occurred validating Bulletin contents."
   attrs = ("msg", )

class AcceptanceGetError(EsxupdateError):
   errno = 37
   description = 'Failed to get the acceptance level configuration.'
   attrs = ('msg',)

class MetadataIOError(EsxupdateError):
   description = 'Error reading metadata.zip'
   errno = 16
   attrs = ('msg',)

class BulletinFormatError(EsxupdateError):
   description = 'The format of the bulletin is invalid'
   errno = 26
   attrs = ('msg',)

class BulletinIOError(EsxupdateError):
   description = 'Error reading or writing Bulletin'
   attrs = ('msg',)

class MetadataBuildError(EsxupdateError):
   description = 'Error in writing a metadata.zip'
   attrs = ('msg',)

class DowngradeError(EsxupdateError):
   description = 'Downgrade to an older version of ESXi is not supported.'
   attrs = ('msg',)

class NormalExit(EsxupdateError):
   description = 'Operation finished successfully.'
   errno = 0
   msg = ''
   attrs = ('msg',)

class NeedsRebootResult(NormalExit):
   description = 'The update completed successfully, ' \
                 'but the system needs to be ' \
                 'rebooted for the changes to be effective.'
   errno = 80
   msg = ''
   attrs = ('msg',)

class HostdRestartResult(NormalExit):
   description = 'Hostd needs to be restarted to complete installation.'
   errno = 81

class HostNotChanged(NormalExit):
   description = 'Host was not updated, no changes required.'
   errno = 82
   attrs = ('msg',)

class LiveInstallOnlyResult(NormalExit):
   description = 'WARNING: Only live system was updated, the change is not persistent.'
   errno = 83

class QuickPatchExit(NormalExit):
   description = 'A Quick Patch remediation has been executed.'
   errno = 84
   attrs = ('msg', 'quickPatchRes')

class ComponentFormatError(EsxupdateError):
   description = 'The format of component is invalid'
   errno = 38
   attrs = ('msg',)

class ComponentIOError(EsxupdateError):
   description = 'Error reading or writing component'
   errno = 39
   attrs = ('msg',)

class ComponentValidationError(EsxupdateError):
   description = 'Error validating component XML or VIB relations'
   errno = 40
   attrs = ('msg', )

class MissingComponentError(EsxupdateError):
   description = "Cannot found components in metadata."
   errno = 42
   attrs = ('msg', )

class ComponentDowngradeError(EsxupdateError):
   description = 'There is one or more component downgraded in the operation'
   errno = 43
   attrs = ('components', 'msg')

class ESXioComponentDowngradeError(ComponentDowngradeError):
   description = 'There is one or more ESXio component downgraded in the ' \
      'operation'
   attrs = ('components', 'msg')

class ConfigComponentDowngradeError(ComponentDowngradeError):
   """This is for image customization where user is trying to retain a lower
      version component having config schema.
   """
   description = 'Component cannot be downgraded as it contains config schema.'
   attrs = ('components', 'msg')

class ComponentConfigDowngrade(ComponentDowngradeError):
   """Sub-class of ComponentDowngradeError specific to config downgrade
      scenario.
   """
   pass

class ReleaseUnitValidationError(EsxupdateError):
   """ Base class for all release unit validation errors
   """
   pass

class AddonValidationError(ReleaseUnitValidationError):
   description = 'Error validating addon metadata or its components'
   errno = 44
   attrs = ('addonID', 'msg',)

class BaseimageValidationError(ReleaseUnitValidationError):
   description = 'Error validating base image metadata or its components'
   errno = 45
   attrs = ('msg',)

class BaseImageNotFound(EsxupdateError):
   description = 'Missing base image in depot'
   errno = 46
   attrs = ('version', 'msg')

class EsxVersionChangeError(EsxupdateError):
   description = 'ESXi version change is not allowed.'
   attrs = ('vib', 'msg')

class AddonNotFound(EsxupdateError):
   description = 'Missing addon in depot'
   attrs = ('name', 'version', 'msg')

class AddonRecalledError(EsxupdateError):
   # TODO: handle this exception in ESX VAPI side.
   description = 'An addon in depot has been recalled'
   attrs = ('name', 'version', 'msg')

class SolutionNotFound(EsxupdateError):
   description = 'Missing solution in depot'
   attrs = ('name', 'version', 'msg')

class HardwareSupportPackageNotFound(EsxupdateError):
   description = 'Missing hardware support package in depot'
   attrs = ('manager', 'package', 'version', 'msg')

class ComponentNotFoundError(EsxupdateError):
   # TODO: handle new attribute to improve UI in VC.
   description = 'Missing component in depot'
   attrs = ('name', 'version', 'msg')

class ComponentRecalledError(EsxupdateError):
   # TODO: handle this exception in ESX VAPI side.
   description = 'A component in depot has been recalled'
   attrs = ('name', 'version', 'msg')

# User removed component related errors.
class RemovedComponentNotFound(EsxupdateError):
   description = 'A component is not found and thus cannot be removed by' \
                 'the user.'
   attrs = ('name', 'msg')

class UnsupportedRemovedComponent(EsxupdateError):
   description = 'A component cannot be removed by the user.'
   attrs = ('name', 'msg')


class IncompatibleSolutionCompsError(EsxupdateError):
   description = ('No compatible combination of multiple solution components '
                  'is found.')
   attrs = ('compNames', 'solutionName', 'msg')

# IncompatibleSolutionComponentError is a special singular case of
# IncompatibleSolutionCompsError with more information.
class IncompatibleSolutionComponentError(IncompatibleSolutionCompsError):
   description = 'Compatible solution component missing in depot'
   attrs = ('name', 'solutionName', 'compVersions', 'msg')

class SolutionComponentNotFound(EsxupdateError):
   description = 'Missing solution components in depot'
   attrs = ('compNames', 'solutionName', 'msg')

class DepotConnectError(EsxupdateError):
   description = 'Unable to connect to depot'
   attrs = ('causes', 'msg')

class ManifestValidationError(ReleaseUnitValidationError):
   description = 'Error validating manifest JSON or its components'
   attrs = ('manifestID', 'msg')

class ManifestConflictError(ReleaseUnitValidationError):
   description = 'Two manifests have a conflict'
   attrs = ('manifestID', 'newManifestID', 'msg')

class MissingVibError(EsxupdateError):
   description = 'Cannot find VIBs in metadata.'
   errno = 47
   attrs = ('vibIDs', 'msg')

class AddonBaseImageMismatchError(EsxupdateError):
   description = 'Addon and baseimage are incompatible'
   attrs = ('addonId', 'baseImageVersion', 'msg')

class ManifestBaseImageMismatchError(EsxupdateError):
   description = ('Hardware support package and baseimage are incompatible')
   attrs = ('manager', 'package', 'version', 'baseImageVersion', 'msg')

class MultipleManifestError(EsxupdateError):
   description = ('Multiple hardware support packages are in software spec.')
   attrs = ('msg', )

class SoftwareSpecFormatError(EsxupdateError):
   description = 'Corrupted or invalid software specification'
   attrs = ('field', 'msg')

class ReleaseUnitConflictError(EsxupdateError):
   description = 'Unequal release units share the same releaseID'
   attrs = ('releaseID', 'releaseType', 'msg')

class MetadataNotFoundError(EsxupdateError):
   description = 'Expected metadata objects are missing in the depots.'
   attrs = ('missingObjs', 'msg')

class ApplyHARebootRequiredError(EsxupdateError):
   description = ('Failed to apply HA due to the host software configuration '
                  'requires a reboot. Reboot the host and try again.')
   attrs = ('msg', )

# Below are inner cause classes of InstallationError, LiveInstallationError, and
# QuickPatchInstallationError.

class ServiceDisableError(EsxupdateError):
   description = 'Failed to disable service during live remediation.'
   attrs = ('component', 'service', 'msg')

class ServiceEnableError(EsxupdateError):
   description = 'Failed to enable service during live remediation.'
   attrs = ('component', 'service', 'msg')

class ComponentUnmountError(EsxupdateError):
   description = 'Failed to unmount a tardisk.'
   attrs = ('component', 'msg')

class MemoryReserveError(EsxupdateError):
   description = 'Failed to reserve memory'
   attrs = ('reserveSize', 'msg')

class PartialVibDownloadError(EsxupdateError):
   description = 'Failed to download Quick Patch contents from a VIB.'
   attrs = ('url', 'msg')

class ResourcePoolFileNotFound(EsxupdateError):
   """A VIB does not have a resource pool definition file.
   """
   description = 'The resource pool definition file is not found.'
   attrs = ('vibID', 'msg')

class ResourcePoolFileInvalid(EsxupdateError):
   """A resource pool definition file is invalid.
   """
   description = 'The resource pool definition file is invalid.'
   attrs = ('rpFile', 'msg')

class ResourcePoolError(EsxupdateError):
   """Error related to creation and deletion of a resource pool.
   """
   description = 'Failed to create or delete a resource pool.'
   attrs = ('rpName', 'msg')

class SecurityPolicyNotFound(EsxupdateError):
   """A VIB does not have a security policy directory.
   """
   description = 'The VIB does not have a security policy directory.'
   attrs = ('vibID', 'msg')

class SecurityPolicyError(EsxupdateError):
   """Error when loading security policy.
   """
   description = 'Failed to load security policy from the directory.'
   attrs = ('policyDir', 'msg')

# Quick Patch incomplete errors indicate to vCenter that while the apply failed
# on this particular host, it can proceed to remediate the next host.
# These errors must share the same "QuickPatchIncomplete" prefix.
class QuickPatchIncompleteError(EsxupdateError):
   """Quick Patch remediation was incomplete due to a failure post tardisk
      mounting.
   """
   description = 'Quick Patch remediation was incomplete due to an error.'
   attrs = ('msg',)

class QuickPatchIncompleteScriptError(EsxupdateError):
   """Quick Patch remediation was incomplete due to a failure in Quick Patch
      script(s).
   """
   description = ('Quick Patch remediation was incomplete due to a Quick Patch '
                  'script failure.')
   attrs = ('scriptNames', 'msg')


class QuickPatchScriptError(EsxupdateError):
   """Quick patch script error, specifically used to handle the error raised
      by executing a Quick Patch script.
   """
   description = 'Failed to execute Quick Patch scripts.'
   attrs = ('scriptNames', 'msg')
   def __str__(self):
      """When printing, omit scriptName, and form a string only by msg, since
         scriptName is included in msg.
      """
      return self.msg

class QuickPatchTpmUnsupportedError(EsxupdateError):
   """Error related to performing Quick Patch on a host with a TPM in active
      status.
   """
   description = 'Failed to perform Quick Patch as the host has enabled TPM.'
   attrs = ('msg',)

class QuickPatchEsxVersionUpdateError(EsxupdateError):
   """Quick patch error while updating vsi node with new ESXi version.
   """
   description = 'Failed to set ESXi version in vsi node'
   attrs = ('msg', )

class QuickPatchUnsupportedError(EsxupdateError):
   """Error when quickpatch is enforced but not supported.
   """
   description = 'Quick Patch is enforced but cannot be performed.'
   attrs = ('msg',)

# Host seeding related Errors

class VibRecreateError(EsxupdateError):
   description = 'Failed to re-create a VIB for depot extraction.'
   attrs = ('vib', 'msg')

class ReservedVibExtractError(EsxupdateError):
   description = 'Failed to add a reserved VIB during depot extraction.'
   attrs = ('vib', 'msg')

class VibChecksumError(EsxupdateError):
   description = ('Checksum of VIB does not match expected during depot '
                  'extraction.')
   attrs = ('vib', 'msg')

class DataStorageNotFound(EsxupdateError):
   description = 'No OSData storage partition is available to extract depot.'
   errno = 48
   attrs = ('msg',)

class SoftwareInfoExtractError(EsxupdateError):
   description = ('An error occurred while extracting image info on the host.')
   attrs = ('msg',)

# For vCenter CPP side error handling.
class DepotNotFoundError(EsxupdateError):
   description = ('Depot not found error.')
   attrs = ('msg', 'urls')

class ResourceError(EsxupdateError):
   description = 'No space available to stage contents'
   errno = 49
   attrs = ('msg')

# DPU related Errors
class DpuInfoError(EsxupdateError):
   description = ('Dpu info error.')
   attrs = ('msg', 'dpuinfo')

class DpuOperationError(EsxupdateError):
   description = 'Error happened during DPU image operation.'
   errno = 50
   attrs = ('msg',)

# Quick Patch related errors
class QuickPatchInstallationError(InstallationError):
   """Quick patch installation error, a sub-class of InstallationError with
      same attributes and __str__ method.
   """
   description = 'Quick patch installation failed.'
   errno = 51

# Schema version incompatibility related errors
class ReleaseUnitSchemaVersionError(ReleaseUnitValidationError):
   description = ('The release unit is incompatible with the current schema '
                  'version of the tool.')
   attrs = ('schemaVer', 'releaseUnitType', 'releaseID',
            'compatibleSchemaVer', 'msg')

   def __str__(self):
        return self.msg
