######################################################################## # Copyright (C) 2019-2020 VMware, Inc. # # All Rights Reserved # ######################################################################## # ''' This module defines the data structure of manifest and implements the functionalities such as construction, serialization to json format, and deserialization from json. ''' from .Addon import Addon from .Errors import ManifestValidationError from .ReleaseUnit import (ATTR_REL_ID, ReleaseUnit, deepcopy) import json try: from .Utils.JsonSchema import ValidateManifest HAVE_VALIDATE_MANIFEST = True except Exception: HAVE_VALIDATE_MANIFEST = False NAME_HSI = 'hardwareSupportInfo' # Prefix for firmware-only manifests that are generated on host. # Presence of the prefix allows the manifest to contain no component. FIRMWARE_ONLY_PREFIX = 'FW-' replaceSpace = lambda x: '_'.join(x.split()) class HardwareSupportManager(object): '''Class for hardware support manager. ''' def __init__(self, name): self.name = name def __eq__(self, other): return (isinstance(other, HardwareSupportManager) and self.name == other.name) class HardwareSupportPackage(object): '''Class for hardware support package. ''' def __init__(self, name, version): self.name = name self.version = version def __eq__(self, other): return (isinstance(other, HardwareSupportPackage) and self.name == other.name and self.version == other.version) class HardwareSupportInfo(object): '''Class for hardware support info. It has a hardware support manager and a hardware support package. ''' def __init__(self, manager, package): self.manager = manager self.package = package def __eq__(self, other): return (isinstance(other, HardwareSupportInfo) and self.manager == other.manager and self.package == other.package) def _DictToHSI(hsiDict): '''Convert dict to HardwareSupportInfo. ''' manager = HardwareSupportManager(hsiDict['manager']['name']) packageDict = hsiDict['package'] package = HardwareSupportPackage(packageDict['name'], packageDict['version']) return HardwareSupportInfo(manager, package) class Manifest(Addon): ''' A hardware manifest is an addon that has extra members for hardware support information. ''' extraAttributes = [NAME_HSI] + Addon.extraAttributes extraDefault = [None] + Addon.extraDefault extraMap = dict(zip(extraAttributes, extraDefault)) mandatoryAttr = list(Addon.mandatoryAttr) mandatoryAttr.extend([NAME_HSI]) # The schema version. SCHEMA_VERSION = "1.0" # Valid schema veriosn map to release version. Need to populate when bump # schema version. SCHEMA_VERSION_MAP = {"1.0": '7.0.0'} # The common software spec type for all instances of this class. releaseType = 'manifest' ReleaseUnit.typeConverters[NAME_HSI] = _DictToHSI @property def isFirmwareOnly(self): """Returns if this manifest is a firmware-only one. """ return self.nameSpec.name.startswith(FIRMWARE_ONLY_PREFIX) def Validate(self, components=None, manifestVibs=None): """Validates the manifest. Manifest should have at least one component and there should be no conflict/obsolete problems within the components. With a firmware-only Hardware Support Package, a manifest object is created on the host, which has no component and removed component name. Parameters: * components - ComponentCollection object having all manifest components. * manifestVibs - VibCollection object with VIBs that correspond to all components in manifest. """ if (not self.isFirmwareOnly and not self.components and not self.removedComponents): raise ManifestValidationError(self.releaseID, 'Manifest should have at least one component or at least remove ' 'one component.') if components and manifestVibs: compProblems = self._getCompProblemMsgs(components, manifestVibs) if compProblems: raise ManifestValidationError(self.releaseID, 'Failed to validate components in manifest %s: %s' % (self.nameSpec.name, ', '.join(compProblems))) @classmethod def FromJSON(cls, jsonString, validation=False): # Schema Validation if validation and HAVE_VALIDATE_MANIFEST: valid, errMsg = ValidateManifest(jsonString) if not valid: try: manifest = json.loads(jsonString) except Exception: # failed to load manifest from jsonstring # hence return empty releaseID raise ManifestValidationError('', errMsg) releaseId = manifest[ATTR_REL_ID] if ATTR_REL_ID in manifest else '' raise ManifestValidationError(releaseId, errMsg) manifest = Manifest(spec=jsonString) if validation: manifest.Validate() return manifest def ToJSON(self): self.Validate() # calling ReleaseUnit class's ToJSON() method to skip the validation # logic implemented in Addon class jsonString = ReleaseUnit.ToJSON(self) # Schema Validation if HAVE_VALIDATE_MANIFEST: valid, errMsg = ValidateManifest(jsonString) if not valid: raise ManifestValidationError(self.releaseID, errMsg) return jsonString def Copy(self): manifest = Manifest() manifestDict = deepcopy(self.ToJSONDict()) manifest.FromJSONDict(manifestDict) return manifest def SetHardwareSupportInfo(self, hsi): if not isinstance(hsi, HardwareSupportInfo): msg = 'Argument value is not an instance of HardwareSupportInfo' raise ValueError(msg) self._hardwareSupportInfo = hsi hardwareSupportInfo = property(lambda self: self._hardwareSupportInfo, SetHardwareSupportInfo)