#!/build/toolchain/lin64/python-3.5.1-openssl1.0.2/bin/python ''' Copyright 2019-2020 VMware, Inc. All rights reserved. -- VMware Confidential ''' ''' epkCommon.py is a repository for common code that is used throughout the various different parts of the EPK. ''' from ctypes import CDLL, RTLD_GLOBAL import os import re import sys import textwrap import json from datetime import datetime from tempfile import NamedTemporaryFile epkBaseDir = os.path.dirname(os.path.dirname(os.path.realpath( os.path.abspath(__file__)))) def SetupSysPath(): """Sets up the various paths to needed imported python modules. """ importPaths = ['site-packages/esximage.zip', 'site-packages/borautils.zip', 'site-packages/lib/ext/' 'jsonschema-2.6.0-py2.py3-none-any.whl', 'site-packages', 'lib64'] for importPath in importPaths: sys.path.insert(0, os.path.join(epkBaseDir, importPath)) def LoadSharedLibs(): """Loads shared libraries needed. """ libDir = os.path.join(epkBaseDir, 'lib64') CDLL(os.path.join(libDir, 'libcrypto.so')) CDLL(os.path.join(libDir, 'libssl.so')) CDLL(os.path.join(libDir, 'libz.so.1')) CDLL(os.path.join(libDir, 'libxml2.so.2')) CDLL(os.path.join(libDir, 'libxslt.so.1')) CDLL(os.path.join(libDir, 'libgcrypt.so.11')) CDLL(os.path.join(libDir, 'libexslt.so.0')) # Our readline is misbuilt - it uses ncursesw symbols, but does # not depend on it. Load it manually, and into global namespace. CDLL(os.path.join(libDir, 'libncursesw.so.5'), mode=RTLD_GLOBAL) CDLL(os.path.join(libDir, 'libreadline.so.6')) def ExitWithMsg(msg, wrapText=True): """Prints an error message and exits the program Params: * msg - The message to print to standard error * wrapText - Set to true to wrap long strings before printing the message. """ if wrapText: msg = textwrap.fill(msg, width=140) if msg: sys.stderr.write(msg + '\n') sys.exit(1) def PromptWithChoice(msg): """Prompts the user with a binary, yes/no choice Params: * msg - The message to prompt the user with. Returns * True if the affirmative choice was chosen by the user, otherwise false """ affirmChoices = ['yes', 'y'] negChoices = ['no', 'n'] prompt = msg + ' [%s/%s].\n' % (affirmChoices[0], negChoices[0]) while True: sys.stdout.write(prompt) choice = (input().strip()).lower() if choice in affirmChoices: return True elif choice in negChoices: return False def GetVibConfigSchemas(vibs, depotConfigSchemas): """Get config schemas of the VIBs, use the known ones in the existing depot when present. """ from vmware.esximage.ConfigSchema import ConfigSchemaCollection from vmware.esximage.Depot import VibDownloader from vmware.esximage.Vib import getConfigSchema vibConfigSchemas = ConfigSchemaCollection() for vib in vibs.values(): csTag = vib.GetConfigSchemaTag() if csTag is None: continue if csTag.schemaId in depotConfigSchemas: vibConfigSchemas.AddConfigSchema(depotConfigSchemas[csTag.schemaId]) continue if vib.name == 'esx-base': # Attempt to re-package esx-base VIB which contains xz'ed # payloads. EPK cannot extract config schema from the VIB # without xz binary at this point. print("Warning: extracting a payload in esx-base VIB is not " "currently supported, skip adding the VIB's config schema " "to depot.") continue with NamedTemporaryFile() as tmpFile: # Open the VIB file to access payloads. VibDownloader(tmpFile.name, vib) try: vib.OpenFile(tmpFile.name) vibConfigSchemas.AddConfigSchema(getConfigSchema(vib, None)) finally: vib.Close() return vibConfigSchemas def CheckVendorName(vendorName): """Checks if Vendor Name is properly formed """ if vendorName.isspace(): errMsg = 'The vendor name cannot be empty' ExitWithMsg(errMsg) if not re.match(r'^[a-zA-Z0-9,. ]{1,20}$', vendorName): errMsg = ('The vendor name must be 1 to 20 alphanumeric characters' ', commas. periods, or spaces. Example: Vmware, Inc') ExitWithMsg(errMsg) def CheckVendorCode(vendorCode): """Checks if Vendor Code is properly formed """ if not re.match(r'^[a-zA-Z0-9]{3}$', vendorCode): errMsg = ('The vendor code must be 3 alphanumeric characters.' ' Example: vmw') ExitWithMsg(errMsg) def ValidateSuppotedBIVersion(suppBIVers): """The function validates the supported base image versions of an addon and manifests. Parameters: * suppBIVers: List of supported base image versions Exception: * ValueError: Raise a value error if the input supported version doesn't match SUPP_BIVER_REG_EXP Returns: * Returns the sanitized list of supported base image versions. """ # Regex to validate supported base image versions # Versions must be of the format X.Y.Z # For ex. 7.0.0, 7.0.1, 7.1.2 etc. SUPP_BIVER_REG_EXP = r'^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+){2}' errMsg = 'Error: The supported base image version must be a ' \ 'non-empty list with the version strings at least matching the ' \ 'format X.Y.Z where, X is a major version, Y is a minor ' \ 'version, and Z is an update version.' if not suppBIVers: ExitWithMsg(errMsg) for ver in suppBIVers: if not re.match(SUPP_BIVER_REG_EXP, ver): ExitWithMsg(errMsg) return suppBIVers def ConfigureDirs(): """Configures the schema and certs dirs for esximage library """ from vmware.esximage import Configure try: Configure(schemadir=os.path.join(epkBaseDir, 'schemas'), jsonschemadir=os.path.join(epkBaseDir, 'json_schemas'), certsdirs=[os.path.join(epkBaseDir, 'testcerts')]) except Exception as err: ExitWithMsg('%s' % str(err)) def GenerateFullReleaseUnitSpec(releaseUnit, spec): '''The function edits the createSpec provided by the clients to make it acceptable to generate releaseUnit. It will add missing internal attributes and validate the attributes values in the createSpec Parameters: * releaseUnit: An object of a sub class of ReleaseUnit, i.e. base image, addon, or manifest * spec: JSON string containing spec data Returns: * JSON string containing all the required attributes to construct the baseImage ''' from vmware.esximage.ReleaseUnit import (ATTR_CATEGORY, ATTR_SCHM_VER, ATTR_ACPT_LVL, ATTR_REL_DATE, JsonParsingError) from vmware.esximage.Addon import ATTR_SUPP_BIVERS try: jsonObj = json.loads(spec) except Exception as err: raise JsonParsingError('Failed to parse json spec, error: %s.' % err) """ TODO: In 7.0, the attributes releaseID and schemaVersion are marked as internal. Eventually this list may increase, hence optimize below code to add any missing attributes """ try: if not ATTR_REL_DATE in jsonObj: jsonObj[ATTR_REL_DATE] = datetime.utcnow().isoformat() jsonObj[ATTR_CATEGORY] = jsonObj[ATTR_CATEGORY].lower() jsonObj[ATTR_ACPT_LVL] = jsonObj[ATTR_ACPT_LVL].lower() # validate supported base image versions if ATTR_SUPP_BIVERS in jsonObj: jsonObj[ATTR_SUPP_BIVERS] = \ ValidateSuppotedBIVersion(jsonObj[ATTR_SUPP_BIVERS]) except KeyError as key: raise AttributeError('Missing attribute %s in the input createSpec.' % key) try: return json.dumps(jsonObj) except Exception as err: raise JsonParsingError('Failed to generate json spec, error: %s.' % err) def GetErrorMsg(error): '''Helper function to extract actual errMsg for error. Parameters: * error: An object of EsxupdateError or Exception. Returns: * An error string representing the error. ''' return getattr(error, 'msg', str(error))