class Config(ConfigBase): """ Configuration management """ def __init__(self, ctx, create=False, failOnDiscoveryError=True): configFile = ctx.ar.config_file self._noFileMsg = f"Configuration file '{configFile}' does not exist" self._cmdSshNws4 = None # Set in _discoverNws4() self._cmdSshHdb = None # Set in _discoverHdb() super().__init__(ctx, './config.yaml.template', configFile, create) if create: return configCacheFile = f'{configFile}.cache' configCacheTimeout = 600 # seconds configMtime = self._getMtime(configFile) # seconds since the Epoch configCacheMtime = self._getMtime(configCacheFile) # self._config = ConfigBase.cleanup(self._getConfigFromFile(configFile)) self._config = self.getObj() configCached = self._read(configCacheFile) if not configCached: # Config cache file does not exist -> discover logging.debug( f"Config cache file '{configCacheFile}' does not exist") self._discoverAndCache(configCacheFile, configCacheTimeout, failOnDiscoveryError) elif self._referenceSystemChanged(configCached): # Reference SAP system changed self._discoverAndCache(configCacheFile, configCacheTimeout, failOnDiscoveryError) elif configMtime > configCacheMtime: # Original config was changed after config was cached logging.debug(f"Configuration file '{configFile}' is newer" f" than cached configuration '{configCacheFile}'") self._discoverAndCache(configCacheFile, configCacheTimeout, failOnDiscoveryError) elif float(configCached['expiryTime']) < self._getCurrentTime(): # Cached config is expired logging.debug('Cached configuration is expired') self._discoverAndCache(configCacheFile, configCacheTimeout, failOnDiscoveryError) else: logging.debug( f"Using cached configuration from '{configCacheFile}'") self._config = configCached logging.debug(f'self._config >>>{self._config}<<<') # Public methods def getFull(self): """ Get full configuration (inlcuding discovered parts) as nested namespace """ logging.debug(f'self._config >>>{self._config}<<<') cleaned = ConfigBase.cleanup(self._config) logging.debug(f'cleaned >>>{cleaned}<<<') # return objToNestedNs(ConfigBase.cleanup(self._config)) return objToNestedNs(self._config) def getImageFlavors(self): """ Get image flavors """ return list(self._config['images'].keys()) def getContainerFlavors(self): """ Get image flavors """ return list(self._config['ocp']['containers'].keys()) # Private methods def _getCurrentTime(self, ): return time.time() def _getMtime(self, file): mtime = -1 if pathlib.Path(file).exists(): mtime = pathlib.Path(file).stat().st_mtime return mtime def _referenceSystemChanged(self, configCached): """ Check whether configured reference system differs from cached reference system """ configuredHost = self._config['refsys']['nws4']['host']['name'] configuredSid = self._config['refsys']['nws4']['sid'].upper() cachedHost = configCached['refsys']['nws4']['host']['name'] cachedSid = configCached['refsys']['nws4']['sidU'] systemChanged = False systemChanged = systemChanged or configuredHost != cachedHost systemChanged = systemChanged or configuredSid != cachedSid if systemChanged: logging.debug( f"Configured reference system '{configuredSid}@{configuredHost}'" f" differs from cached reference system '{cachedSid}@{cachedHost}'" ) return systemChanged def _discoverAndCache(self, configCacheFile, configCacheTimeout, failOnDiscoveryError): logging.debug('Running configuration discovery') # self._checkRequiredOptional() try: self._discover() self._config['expiryTime'] = str(self._getCurrentTime() + configCacheTimeout) logging.debug(f"Writing config to cache file '{configCacheFile}'") # pylint: disable=unspecified-encoding with open(configCacheFile, 'w') as ccfh: yaml.dump(self._config, stream=ccfh) except _DiscoveryError as derr: message = '\nThe following problem occured during configuration discovery:\n\n' message += f"{'-'*65}\n" message += f'{derr}\n' message += f"{'-'*65}\n" if failOnDiscoveryError: fail(message) else: message += '\nThe configuration cache file was not written. Proceeding with\n' message += 'possibly incomplete configuration. This may lead to runtime\n' message += 'errors. Check your configuration file\n\n' message += f" {self._instanceFile }\n\n" message += 'and correct the error.\n\n' warn(message) def _discover(self): self._config['images'] = {} self._discoverNfs() self._discoverInit() self._discoverNws4() self._discoverHdb() self._discoverOcp() def _getInstno(self, cmdSsh, sidU, instPrefix, host): cmd = f'grep -E "SAPSYSTEM +" /usr/sap/{sidU}/SYS/profile/{sidU}_{instPrefix}*_{host}' result = cmdSsh.run(cmd) if result.rc > 0: raise _DiscoveryError( f"Could not discover instance number of {sidU} on host {host}\n" f"The profile" f" /usr/sap/{sidU}/SYS/profile/{sidU}_{instPrefix}*_{host}" f" might not exist.\n" f"Check if the parameters you specified for your SAP reference" f" system are valid.") return result.out.split('\n')[0].split()[2] def _getTimeZone(self, cmdSsh): return cmdSsh.run( 'timedatectl | grep Time | cut -d ":" -f2 | cut -d " " -f2').out def _getHostByName(self, host): try: ipAddr = socket.gethostbyname(host) except Exception as nameResolutionError: raise _DiscoveryError( f"Cannot resolve IP address for '{host}'\n" f"Ensure that hostname '{host}' is correct,\n" f"and check the name resolution record of it." ) from nameResolutionError return ipAddr def _getImageNames(self, flavor): if flavor == 'init': short = 'soos-init' else: short = f'soos-{self._config["refsys"][flavor]["sidL"]}' local = f'localhost/{short}:latest' ocp = f'default-route-openshift-image-registry.apps.{self._config["ocp"]["domain"]}' ocp += f'/{self._config["ocp"]["project"]}/{short}:latest' return {'short': short, 'local': local, 'ocp': ocp} def _getContainerName(self, containerFlavor): if containerFlavor in ['init', 'hdb']: shortName = self._config['images'][containerFlavor]['names'][ 'short'] elif containerFlavor in ['di', 'ascs']: shortName = f"{self._config['images']['nws4']['names']['short']}-{containerFlavor}" else: raise _DiscoveryError( f"Unknown container flavor '{containerFlavor}'") return shortName def _discoverInit(self): # Image names self._config['images']['init'] = {'names': self._getImageNames('init')} def _discoverNws4(self): # Host and <sid>adm user host = self._config['refsys']['nws4']['host']['name'] sidL = self._config['refsys']['nws4']['sid'].lower() sidU = self._config['refsys']['nws4']['sid'].upper() self._config['refsys']['nws4']['sidL'] = sidL self._config['refsys']['nws4']['sidU'] = sidU del self._config['refsys']['nws4']['sid'] user = self._ctx.cr.refsys.nws4.sidadm if user.name != f'{sidL}adm': raise _DiscoveryError( f"Mismatch between credentials file nws4 user name '{user.name}'\n" f"and derived configuration file nws4 user name '{sidL}adm.'\n" f"Check credentials file parameter 'refsys.nws4.sidadm.name' and\n" f"configuration file parameter 'refsys.nws4.sid' and correct the\n" f"wrong value.") self._config['refsys']['nws4']['host']['ip'] = self._getHostByName( host) self._cmdSshNws4 = CmdSsh(self._ctx, host, user, check=False) if self._cmdSshNws4.passwordNeeded(): print(f"Enter password for user {user.name} running on {host}") res = self._cmdSshNws4.run('true') if res.rc != 0: msg = self._cmdSshNws4.formatSshError(res, host, user) raise _DiscoveryError( f"{msg}\n\n" "In addition neither the\n" " - SAP SID of the HANA instance\n" "nor the\n" " - the hostname on which the HANA instance is running\n" "can be discovered\n") # User and group ID of <sid>adm (uid, gid) = self._cmdSshNws4.run( f'grep "{user.name}" /etc/passwd').out.split(':')[2:4] self._config['refsys']['nws4']['sidadm'] = {'uid': uid, 'gid': gid} # Time zone self._config['refsys']['nws4']['timezone'] = self._getTimeZone( self._cmdSshNws4) # sapmnt base directory self._config['refsys']['nws4']['base'] = {} self._config['refsys']['nws4']['base']['sapmnt'] = self._getSapmntDir( sidU) # SAPFQDN defaultProfile = self._getDefaultProfile(sidU) result = self._cmdSshNws4.run(f'grep "^SAPFQDN" {defaultProfile}') if result.rc > 0: logging.warning("Could not discover SAPFQDN " f"from {defaultProfile}") self._config['refsys']['nws4']['sapfqdn'] = "" else: self._config['refsys']['nws4']['sapfqdn'] = result.out.split( '\n')[0].split()[2] # Instance specific parameters ascsInstno = self._getInstno(self._cmdSshNws4, sidU, 'ASCS', host) diInstno = self._getInstno(self._cmdSshNws4, sidU, 'D', host) self._config['refsys']['nws4']['ascs'] = { # Instance number 'instno': ascsInstno, # Default profile name 'profile': f'{sidU}_ASCS{ascsInstno}_{host}' } self._config['refsys']['nws4']['di'] = { # Instance number 'instno': diInstno, # Default profile name 'profile': f'{sidU}_D{diInstno}_{host}' } # Image names self._config['images']['nws4'] = {'names': self._getImageNames('nws4')} # Set optional package names to be installed self._config['images']['nws4']['packages'] = [] def _discoverHdb(self): self._config['refsys']['hdb'] = {} host = self._discoverHdbHost() sid = self._discoverHdbSid() sidL = sid.lower() sidU = sid.upper() # self._config['refsys']['hdb']['sid'] = sid self._config['refsys']['hdb']['sidL'] = sidL self._config['refsys']['hdb']['sidU'] = sidU user = self._ctx.cr.refsys.hdb.sidadm if user.name != f'{sidL}adm': raise _DiscoveryError( f"Mismatch between credentials file hdb user name '{user.name}'\n" f"and derived configuration file hdb user name '{sidL}adm.'\n" f"Check credentials file parameter 'refsys.hdb.sidadm.name' and\n" f"configuration file parameter 'refsys.hdb.sid' and correct the\n" f"wrong value.") self._config['refsys']['hdb']['host'] = { 'name': host, 'ip': self._getHostByName(host) } self._cmdSshHdb = CmdSsh(self._ctx, host, user, check=False) if self._cmdSshHdb.passwordNeeded(): print(f"Enter password for user {user.name} running on {host}") res = self._cmdSshHdb.run('true') if res.rc != 0: msg = self._cmdSshHdb.formatSshError(res, host, user) raise _DiscoveryError(f"{msg}\n\n") # User and group ID of <sid>adm # Must be performed on HDB host! result = self._cmdSshHdb.run(f'grep "{user.name}" /etc/passwd') if result.rc > 0: raise _DiscoveryError( f"Could not discover uid and gid for user {user.name}.") (uid, gid) = result.out.split(':')[2:4] self._config['refsys']['hdb']['sidadm'] = {'uid': uid, 'gid': gid} # Time zone self._config['refsys']['hdb']['timezone'] = self._getTimeZone( self._cmdSshHdb) # Instance specific parameters # Must be performed on HDB host! # Instance number self._config['refsys']['hdb']['instno'] = self._getInstno( self._cmdSshHdb, sidU, 'HDB', host) # HDB host rename nws4HostName = self._config['refsys']['nws4']['host']['name'] hdbHostName = self._config['refsys']['hdb']['host']['name'] if nws4HostName == hdbHostName: self._config['refsys']['hdb']['rename'] = 'no' else: self._config['refsys']['hdb']['rename'] = 'yes' # HDB base directories self._config['refsys']['hdb']['base'] = {} self._config['refsys']['hdb']['base'][ 'shared'] = self._discoverHdbBaseShared(sidU) self._config['refsys']['hdb']['base'][ 'data'] = self._discoverHdbBaseData(sidU) self._config['refsys']['hdb']['base'][ 'log'] = self._discoverHdbBaseLog(sidU) # Image names self._config['images']['hdb'] = {'names': self._getImageNames('hdb')} # Set optional packages packages = self._discoverHdbOptPkgs() logging.debug(f'Optional packages for hdb: {packages}') self._config['images']['hdb']['packages'] = packages def _discoverNfs(self): if not self._config['nfs']['host']['name']: self._config['nfs']['host']['name'] = self._config['ocp'][ 'helper']['host']['name'] hostIp = self._getHostByName(self._config['nfs']['host']['name']) self._config['nfs']['host']['ip'] = hostIp def _discoverOcp(self): project = self._config['ocp']['project'] hostIp = self._getHostByName( self._config['ocp']['helper']['host']['name']) self._config['ocp']['helper']['host']['ip'] = hostIp self._config['ocp']['sa'] = { 'name': f'{project}-sa', 'file': f'{project}-service-account.yaml' } # Containers self._config['ocp']['containers']['init'] = {} self._config['ocp']['containers']['init'][ 'name'] = self._getContainerName('init') self._config['ocp']['containers']['hdb'][ 'name'] = self._getContainerName('hdb') self._config['ocp']['containers']['ascs'][ 'name'] = self._getContainerName('ascs') self._config['ocp']['containers']['di'][ 'name'] = self._getContainerName('di') # Set requested resources for containers logging.debug(f'config >>>{yaml.dump(self._config)}<<<') # Memory for HDB container # # discovered size for HDB container: # size of the HANA filesystem # Value for both limits and requests are set to discovered size # if no value specified in configuration hdbMinMem = f'{self._discoverHdbSizeGiB()}Gi' logging.debug(f'config >>>{yaml.dump(self._config)}<<<') for kind in ('requests', 'limits'): res = self._config['ocp']['containers']['hdb']['resources'][kind] if not res['memory']: res['memory'] = hdbMinMem logging.warning(getMessage("msgL001", kind, "HDB", hdbMinMem)) # Memory for NWS4 Dialog Instance container # # discovered size for NWS4 DI container: # PHYS_MEMSIZE if available in Instance Profile # or 10 percent of physical memory size of reference system, at least 32GiB # Value for both limits and requests are set to discovered size # if no value specified in configuration diMinMem = f'{self._discoverDiSizeGiB()}Gi' logging.debug(f'config >>>{yaml.dump(self._config)}<<<') for kind in ('requests', 'limits'): res = self._config['ocp']['containers']['di']['resources'][kind] if not res['memory']: res['memory'] = diMinMem logging.warning( getMessage("msgL001", kind, "Dialog Instance", diMinMem)) def _discoverHdbSid(self): defaultProfile = self._getDefaultProfile( self._config['refsys']['nws4']['sidU']) cmd = f'grep dbs/hdb/dbname {defaultProfile}' result = self._cmdSshNws4.run(cmd) if result.rc > 0: raise _DiscoveryError( f"Could not discover HANA SID from {defaultProfile}") return result.out.split('=')[1].strip() def _discoverHdbHost(self): defaultProfile = self._getDefaultProfile( self._config['refsys']['nws4']['sidU']) cmd = f'grep SAPDBHOST {defaultProfile}' result = self._cmdSshNws4.run(cmd) if result.rc > 0: raise _DiscoveryError( f"Could not discover SAPDBHOST from {defaultProfile}") return result.out.split('=')[1].strip() def _discoverHdbBaseShared(self, sidU): profile = f'/usr/sap/{sidU}/SYS/profile' out = self._cmdSshHdb.run(f'readlink {profile}').out # example for out: # /hana/shared/SID/profile # after splitting it: # ['','hana','shared','SID','profile'] # We ignore the last three components return '/'.join(out.split('/')[:-3]) def _discoverHdbBaseData(self, sidU): return self._getBasePathFromGlobalIni(sidU, 'data') def _discoverHdbBaseLog(self, sidU): return self._getBasePathFromGlobalIni(sidU, 'log') def _discoverHdbSizeGiB(self): """ Discover storage in GiB needed for HDB content """ sidU = self._config['refsys']['hdb']['sidU'] dataDir = f"{self._config['refsys']['hdb']['base']['data']}/data/{sidU}" out = self._cmdSshHdb.run(f'du -s -B 1G {dataDir} | cut -f1').out return int(out) + self._ctx.cs.additionalFreeSpaceHdbGiB def _discoverHdbOptPkgs(self): # to get the version of the HANA DB, we need the path to the instance directory sidU = self._config['refsys']['hdb']['sidU'] instno = self._config['refsys']['hdb']['instno'] spsLevel = getSpsLevelHdb(self._cmdSshHdb, sidU, instno) logging.debug(f'HANA DB SPS Level: {spsLevel}') optionalHdbPkgs = [] for pkg in self._ctx.cs.optionalHdbPkgs: if pkg.minSpsLevel <= spsLevel < pkg.maxSpsLevel: logging.debug( f'Optional package to be installed: {pkg.packageName}') logging.debug(f'enabling repository: {pkg.repository}') pkg.dnfInstallable = isRepoAccessible(pkg.repository) optionalHdbPkgs.append(pkg) return optionalHdbPkgs def _discoverDiSizeGiB(self): """ Discover storage in GiB needed for Dialog Instance """ memsize = self._discoverDiSizeFromInstProfileGiB() if memsize > 0: return memsize return max(self._discoverDiSizeFromRefHostGiB(), self._ctx.cs.minMemSizeDIGiB) def _discoverDiSizeFromInstProfileGiB(self): profile = self._getInstanceProfile() memsizeInMiB = self._cmdSshNws4.run( f'grep PHYS_MEMSIZE {profile} | cut -d = -f2').out if not memsizeInMiB: return 0 return int(memsizeInMiB) // 1024 def _discoverDiSizeFromRefHostGiB(self): # Output of 'grep MemTotal /proc/meminfo' looks like: # MemTotal: 64819648 kB # cmd = "grep MemTotal /proc/meminfo " cmd += "| cut -d : -f2 " memsizeInKb = int(self._cmdSshNws4.run(cmd).out.split()[0]) memsizeInGiB = memsizeInKb // 1024 // 1024 # The size is set to 10% of MemTotal (according to SAP Settings) return memsizeInGiB // 10 def _getSapmntDir(self, sidU): profilePath = self._cmdSshNws4.run(f'find /usr/sap/ -type l -ipath ' f'"*{sidU}/SYS/profile"').out profileTarget = self._cmdSshNws4.run(f'readlink "{profilePath}"').out return profileTarget[0:profileTarget.index(f'/{sidU}/profile')] def _getInstanceProfile(self): sidU = self._config['refsys']['nws4']['sidU'] profile = f'/usr/sap/{sidU}/SYS/profile/' profile += self._config['refsys']['nws4']['di']['profile'] return profile def _getDefaultProfile(self, sidU): return f'/usr/sap/{sidU}/SYS/profile/DEFAULT.PFL' def _getBasePathFromGlobalIni(self, sidU, baseType): if baseType not in ['data', 'log']: raise _DiscoveryError("Internal error: wrong baseType specified") # There exist more than on location of the global.ini # They are ordered in different layers: # Default # System # Database # Host # The parameters are taken from top to bottom. # https://help.sap.com/viewer/6b94445c94ae495c83a19646e7c3fd56/2.0.04/en-US/3f1a6a7dc31049409e1a9f9108d73d51.html instno = self._config['refsys']['hdb']['instno'] hostname = self._config['refsys']['hdb']['host']['name'] sapmnt = self._config['refsys']['hdb']['base']['shared'] locationlist = [ f"/usr/sap/{sidU}/HDB{instno}/exe/config", f"{sapmnt}/{sidU}/SYS/global/hdb/custom/config", f"/usr/sap/{sidU}/SYS/global/hdb/custom/config", f"{sapmnt}/{sidU}/SYS/global/hdb/custom/config/DB_{sidU}", f"/usr/sap/{sidU}/SYS/global/hdb/custom/config/DB_{sidU}", f"/usr/sap/{sidU}/HDB{instno}/{hostname}" ] for location in locationlist: basepath = f"basepath_{baseType}volumes" cmd = f'grep "{basepath}[= ]" {location}/global.ini' result = self._cmdSshHdb.run(cmd) if result.rc == 0: # Example for result.out # basepath_datavolumes = /sapmnt/hana/data/HD1 # We need the basepath, which means: # /sapmnt/hana # Get first the complete path itself tempPath = result.out.split('=')[1].strip() # Then the basepath itself path = '/'.join(tempPath.split('/')[:-2]) # It might happen that the path contains a SAP profile/environment variable # such as $(DIR_GLOBAL). This must be replaced. # The parameter value can be got from call sappfpar path = self._replaceSAPPfpar(sidU, path) if not path: raise _DiscoveryError( f"Could not get the base path for {basepath} from one of the global.ini files" ) return path def _replaceSAPPfpar(self, sidU, path): if "$(" not in path: return path subDirs = path.split('/') for subDir in subDirs: if "$(" in subDir: sappfpar = subDir value = getSAPPfparValue(self._cmdSshHdb, sidU, "hdb", sappfpar) if not value: raise _DiscoveryError( f"Could not get value for {sappfpar} from sappfpar call" ) subDirs[subDirs.index(sappfpar)] = value break return '/'.join(subDirs)
class Verify(): """ Verify various configuration settings """ def __init__(self, ctx): self._functions = { "verify_ocp": self._verifyOcp, "verify_nfs": self._verifyNfs, "verify_nws4": self._verifyNws4, "verify_hdb": self._verifyHdb, } self._ctx = ctx self._cmdSshNfs = CmdSsh(ctx, ctx.cf.nfs.host.name, ctx.cr.nfs.user, check=False, reuseCon=False) self._cmdSshNws4 = CmdSsh(ctx, ctx.cf.refsys.nws4.host.name, ctx.cr.refsys.nws4.sidadm, check=False, reuseCon=False) self._cmdSshHdb = CmdSsh(ctx, ctx.cf.refsys.hdb.host.name, ctx.cr.refsys.hdb.sidadm, check=False, reuseCon=False) self._ocp = Ocp(self._ctx, login="******", verify=True) # Public methods def verify(self): """ Verify various configuration settings """ success, msg = self._checkSsh() if not success: fail(msg) showMsgOk("SSH key setup is valid.") if self._ctx.ar.function in self._functions: return self._functions[self._ctx.ar.function]() success = self._verifyOcp() and success success = self._verifyImages() and success success = self._verifyNws4() and success success = self._verifyHdb() and success success = self._verifyNfs() and success success = self._verifySapSystem() and success return success # Private methods def _checkSsh(self): success = True msg = '' res = self._cmdSshNfs.run('true') if res.rc != 0: msg += self._cmdSshNfs.formatSshError(res, self._ctx.cf.nfs.host.name, self._ctx.cr.nfs.user) success = False res = self._cmdSshNws4.run('true') if res.rc != 0: msg += self._cmdSshNws4.formatSshError(res, self._ctx.cf.refsys.nws4.host.name, self._ctx.cr.refsys.nws4.sidadm) success = False res = self._cmdSshHdb.run('true') if res.rc != 0: msg += self._cmdSshHdb.formatSshError(res, self._ctx.cf.refsys.hdb.host.name, self._ctx.cr.refsys.hdb.sidadm) success = False return success, msg def _verifyOcp(self): """ Verify OCP settings """ # pylint: disable=too-many-statements def areResourcesValid(ocp, containerType): return areContainerMemResourcesValid(ocp, containerType) def isSecretExisting(ctx): return ctx.cf.ocp.containers.di.secret in self._ocp.getSecret() def isHdbSecretValid(ctx): userInSecret = self._ocp.getHdbConnectSecretUser() userInCreds = ctx.cr.refsys.nws4.hdbconnect if ( not userInSecret.name == userInCreds.name or not userInSecret.password == userInCreds.password ): return False return True def verifySetup(): success = True if self._ocp.isDomainValid(): showMsgOk("OCP domain name is valid.") if self._ocp.isCredentialsValid(): showMsgOk("OCP user and password are valid.") if self._ocp.isProjectValid(): showMsgOk("OCP project is valid.") else: showMsgErr(f"OCP project '{ocp.project}' does not exist.") success = False else: showMsgErr(f"OCP user '{user.name}' and/or password are invalid.") success = False else: showMsgErr(f"OCP domain name '{ocp.domain}' is invalid.") success = False return success def verifyResources(ocp): success = True for containerType in self._ctx.config.getContainerFlavors(): if containerType == 'init': continue if areResourcesValid(ocp, containerType): showMsgOk("OCP memory resources for container type " f"'{containerType}' are valid.") else: showMsgErr(f"OCP memory limit for container type '{containerType}' " f"is less than the value specified for requested memory.") success = False return success def verifySecret(ocp): success = True if not refSystemIsStandard(self._ctx): secret = ocp.containers.di.secret if secret: if isSecretExisting(self._ctx): if isHdbSecretValid(self._ctx): showMsgOk(f"OCP secret '{secret}' exists and is valid.") else: showMsgErr(f"Mismatch between generated secret '{secret}' " "and values specified in your credentials file.") showMsgInd("Re-generate your secret by executing the tool " "'tools/ocp-hdb-secret-gen'") success = False else: showMsgErr(f"Specified OCP secret '{secret}' " "was not found in OCP cluster.") showMsgInd("Make sure the secret exists and is " "created in the right project.") success = False else: showMsgErr("Reference system is a distributed system.") showMsgInd("You must specify the name of an OCP secret in the config.yaml file") showMsgInd("containing the information about the " "SAP HANA DB user and password.") success = False return success ocp = self._ctx.cf.ocp user = self._ctx.cr.ocp.user success = verifySetup() success = success and verifyResources(ocp) success = success and verifySecret(ocp) return success def _verifyImages(self): """ verify Settings for images """ def _isRpmFileForPackageAvailable(packageName, path): try: getRpmFileForPackage(packageName, path) return True except RpmFileNotFoundException as exp: print(exp.errorText) return False def _getImageTypes(ctx): return list(ctx.cf.images.__dict__) success = True defaultPackagesDir = self._ctx.cs.defaultPackagesDir for flavor in _getImageTypes(self._ctx): if flavor == "init": continue packages = getattr(self._ctx.cf.images, flavor).packages for package in packages: if package.dnfInstallable: showMsgOk(f"Package {package.packageName} installable via dnf install.") else: if _isRpmFileForPackageAvailable(package.packageName, defaultPackagesDir): showMsgOk(f"Package {package.packageName} installable via rpm.") else: showMsgErr(f"Package {package.packageName} not found " "in {defaultPackagesDir}.") success = False return success def _verifyNfs(self): """ Verify NFS settings """ nfs = self._ctx.cf.nfs user = self._ctx.cr.nfs.user success = True if self._isHostNameValid(self._cmdSshNfs): showMsgOk("NFS host is valid.") if self._isUserValid(self._cmdSshNfs): showMsgOk("NFS user is valid.") else: showMsgErr(f"NFS user '{user.name}' is invalid " f"or ssh is not set up correctly.") showMsgInd(f"Check first the existence of '{user.name}' on '{nfs.host.name}'.") showMsgInd(f"If exists, check the ssh connection by executing: " f"ssh {user.name}@{nfs.host.name}") success = False else: showMsgErr(f"NFS host '{nfs.host.name}' is invalid.") success = False return success def _verifyNws4(self): """ Verify settings for reference system component 'nws4' """ return self._verifyRefSys('nws4', self._cmdSshNws4) def _verifyHdb(self): """ Verify settings for reference system component 'hdb' """ success = self._verifyRefSys('hdb', self._cmdSshNws4) if success: for baseDir in ['shared', 'data', 'log']: basePath = getattr(self._ctx.cf.refsys.hdb.base, baseDir) if self._isHdbBaseDirValid(baseDir): showMsgOk(f"HDB base directory '{basePath}' is valid for {baseDir}.") else: showMsgErr(f"HDB base directory '{basePath}' is invalid.") success = False return success def _verifyRefSys(self, component, cmdSsh): """ Verify settings for given component' """ compUp = component.upper() sidU = getattr(self._ctx.cf.refsys, component).sidU hostname = getattr(self._ctx.cf.refsys, component).host.name user = getattr(self._ctx.cr.refsys, component).sidadm success = True if self._isHostNameValid(cmdSsh): showMsgOk(f"{compUp} host is valid.") if self._isUserValid(cmdSsh): showMsgOk(f"{compUp} user is valid.") if self._isSidInUsrSapServices(cmdSsh, sidU): showMsgOk(f"{compUp} SAP system ID is valid.") else: showMsgErr(f"{compUp} SAP system ID is invalid.") success = False else: showMsgErr(f"{compUp} user '{user.name}' is invalid " f"or ssh is not set up correctly.") showMsgInd(f"Check first the existence of '{user.name}' on '{hostname}'.") showMsgInd(f"If exists, check the ssh connection by executing: " f"ssh {user.name}@{hostname}") success = False else: showMsgErr(f"{compUp} host '{hostname}' is invalid.") success = False return success def _verifySapSystem(self): """ Verify SAP system setup """ success = True if refSystemIsStandard(self._ctx): if not self._ctx.cf.refsys.nws4.host.name == self._ctx.cf.refsys.hdb.host.name: success = False showMsgErr(f"The HANADB database '{self._ctx.cf.refsys.hdb.sidU}' " "must run on the same host as the NWS4 SAP System.") if not self._isHdbSidInDefaultPfl(): showMsgErr("You must not use a different HANADB SAP System " f"than specified for the NWS4 SAP System '{self._ctx.cf.refsys.nws4.sidU}'.") success = False return success def _isHostNameValid(self, cmdSsh): out = self._checkSshLogin(cmdSsh) return 'Could not resolve hostname' not in out def _isUserValid(self, cmdSsh): out = self._checkSshLogin(cmdSsh) return 'Permission denied' not in out and 'Connection reset' not in out def _checkSshLogin(self, cmdSsh): return cmdSsh.run('true').err def _isSidInUsrSapServices(self, cmdSsh, sidU): out = cmdSsh.run(f' grep {sidU} /usr/sap/sapservices | wc -l').err return not out.startswith('0') def _isDirValid(self, cmdSsh, directory): out = cmdSsh.run(f' ls {directory}').err return 'No such file or directory' not in out def _isHdbBaseDirValid(self, base): basePath = getattr(self._ctx.cf.refsys.hdb.base, base) out = self._cmdSshHdb.run(f' ls {basePath}').out return base in out def _isHdbSidInDefaultPfl(self): defaultPfl = f'/usr/sap/{self._ctx.cf.refsys.nws4.sidU}/SYS/profile/DEFAULT.PFL' out = self._cmdSshNws4.run(f' grep dbs/hdb/dbname {defaultPfl}').out return self._ctx.cf.refsys.hdb.sidU in out