def parse(self, content): r''' Parse version information per process that is identified by its PID File content is just a trace file which contains information per process. @types: str -> list[ConfigInTrace] ''' assert content initParser = sap_discoverer.IniParser() tranches = [] # x) split content onto chunks per process # separator is "trc file:" for chunk in str(content).strip().split('trc file:'): parts = chunk.split('F ignore unrecognized options', 1) # only first part is interesting (version and arguments information) usefulSection = parts and parts[0] or chunk sections = usefulSection.split('arguments :', 1) if sections: # agent version information VERSION_PART = 0 result = initParser.parseValueByNameMapping( sections[VERSION_PART], keyTransformationFn=string.lower, valueTransformationFn=string.strip, nameValueSeparator=' ') if result: pid = result.get('pid') versionInfo = sap.VersionInfo(result.get('relno'), result.get('patchno'), result.get('patchlevel'), result.get('make')) # arguments section jstartVersionInfo = None ARGUMENTS_PART = 1 if len(sections) > 1: regexp = r'''.*?arg\[\s*5\s*\]\s*= # 5th arguemnt \s*\-DSAPJStartVersion\s*= # java system protoperty \s*(\d+)\s*, # release information (group 1) \s*patch\s*(\d+)\s*, # patch information (2) (.*) # version description (3) ''' matchObj = re.search(regexp, sections[ARGUMENTS_PART], re.X | re.I) jstartVersionInfo = matchObj and sap.VersionInfo( matchObj.group(1), matchObj.group(2), description=matchObj.group(3).strip()) tranches.append( self.ConfigInTrace(pid, versionInfo, jstartVersionInfo)) return tranches
def _parseVersionInfoFromIniResult(self, result): r'@types: sap_discoverer.IniDoc -> sap_trex.VersionInfo' if not result: raise ValueError("Input is empty") version = result.get('saptrex version') matchObj = re.match('(\d+?\.\d+?)\.', version) # value has format like 7.20.09.297333 release = matchObj and matchObj.group(1) or version return sap.VersionInfo(release, description=version)
def parseResultItem(result): r''' @types: ResultSet -> tuple[str, sap.SoftwareComponent] @return: pair of system name and software component ''' name = result.getString("COMPONENT") sysName = GetComponents._parseSystemName(result) release = result.getString("SAPRELEASE") patchLevel = result.getString("EXTRELEASE") type_ = GetComponents._parseType(result) versionInfo = sap.VersionInfo(release, patchLevel=patchLevel) return (sysName, sap.SoftwareComponent(name, type_, None, versionInfo))
def __parse(self, items): r'@types: ? -> list[InstanceInfo]' servers = [] for i in xrange(items.getRowCount()): name = items.getCell(i, 0) logger.debug('server name: %s' % items.getCell(i, 0)) logger.debug('server hostname: %s' % items.getCell(i, 1)) logger.debug('server ip: %s' % items.getCell(i, 4)) try: logger.debug("Process server: %s" % name) hostname, sid, number = self._decomposeServerName(name) # get instance name from start profile path startPfPath = items.getCell(i, 9) instancePfPath = items.getCell(i, 10) instance = self._extractInstanceInfoFromProfilePath( sid, startPfPath or instancePfPath) instance = sap.Instance(instance.name, number, hostname, startPfPath=startPfPath, instancePfPath=instancePfPath) sapSystem = sap.System(sid) # host-info ip = sap.createIp(items.getCell(i, 4)) hostname = items.getCell(i, 1) or hostname address = sap.Address(hostname, (ip, )) host = self.Host(address, osInfo=items.getCell(i, 2), machineType=items.getCell(i, 3)) # server-info startDate = self._parseStartDate(items.getCell(i, 5)) versionInfo = sap.VersionInfo(items.getCell(i, 6), patchLevel=items.getCell(i, 8)) instanceInfo = self.createInstanceInfo( instance, sapSystem, host, homeDirPath=items.getCell(i, 11), dbLibraryInfo=items.getCell(i, 7), codePage=items.getCell(i, 12), numberOfProcesses=items.getCell(i, 13), versionInfo=versionInfo, startDate=startDate) servers.append(instanceInfo) except Exception, e: logger.warnException("%s. %s" % (name, str(e)))
def getVersionInfo(self, filePath): '@types: str -> sap.VersionInfo' output = self.shell.execCmd('"%s" -v' % filePath) if output and output.strip(): versionData = re.match( '.*CCMS version\s+(.*?)' 'compiled.*relno\s+(\d)(\d+).*' 'patch text\s+(.*?)\n.*' 'patchno\s+(\d+)', output, re.DOTALL) release = "%s.%s" % (versionData.group(2), versionData.group(3)) patchNumber = versionData.group(5) description = '%s, %s, %s, patch number %s' % ( release, versionData.group(1).strip(), versionData.group(4).strip(), patchNumber) return sap.VersionInfo(release, patchNumber, description=description)
def parseVersionInfo(self, result): r'''@types: command.Result -> sap.VersionInfo''' output = result.output valueByName = sap_discoverer.IniParser().parseValueByNameMapping( # output prefixed with description to get first line "description=%s" % output.strip(), valueTransformationFn=string.strip) kernelPatchLevel = None sourceId = valueByName.get('source id') level_match = re.match(re.compile(r'\d+\.(\d+)'), sourceId) if level_match: kernelPatchLevel = level_match.group(1) return sap.VersionInfo( valueByName.get('kernel release'), valueByName.get('patch number'), valueByName.get('update level'), valueByName.get('compiled on'), kernelPatchLevel )
def parse(self, output): '@types: str -> sap.VersionInfo?' file_content_lines = output.splitlines() kernel_release = None patch_number = None patch_level = None for line in file_content_lines: level_match = (self.PATCH_LEVEL_REGEX and re.match(self.PATCH_LEVEL_REGEX, line)) match = re.match(self.RELEASE_VERSION_REGEX, line) if match: kernel_release = match.group(1) elif level_match: patch_level = level_match.group(1) else: found_patch_number = re.match(self.PATCH_NUMBER_REGEX, line) if found_patch_number: patch_number = found_patch_number.group(1) if kernel_release: return sap.VersionInfo(kernel_release, patchNumber=patch_number, patchLevel=patch_level)
def getComponents(self): r''' Get list of software components available in the system @types: -> list[sap.SoftwareComponent] ''' resultSet = self.getJcoClient().executeQuery( "CVERS", None, "COMPONENT,RELEASE,EXTRELEASE,COMP_TYPE" ) # @@CMD_PERMISION sap protocol execution components = [] while resultSet.next(): componentType = resultSet.getString("COMP_TYPE") try: release = resultSet.getString("RELEASE") components.append( sap.SoftwareComponent( resultSet.getString("COMPONENT"), componentType, versionInfo=(release and sap.VersionInfo( release, patchLevel=resultSet.getString("EXTRELEASE")) or None))) except sap.SoftwareComponent.TypeIsNotRecognized, ex: logger.warn("%s: %s" % (str(ex), componentType)) except:
def process(self, context): r''' @types: applications.ApplicationSignatureContext ''' # ------------------------------------------------------------ DISCOVERY "SAP TREX plug-in DISCOVERY start" | info shell = context.client fs = file_system.createFileSystem(shell) pathtools = file_system.getPath(fs) # x) get process related application hostOsh = context.hostOsh application = context.application destinationIp = application.getConnectionIp() "x) Find TREX Daemon process that has profile path as parameter" | info mainProcess = findFirst(isMainTrexProcess, context.application.getProcesses()) profilePath = sap_discoverer.getProfilePathFromCommandline( mainProcess.commandLine) "x) Read profile content: %s" % profilePath | info getFileContent = Sfn( Fn(self.__getFileWithContent, shell, pathtools, __)) profileFile = profilePath and getFileContent(profilePath) if not profileFile: "Plug-in flow broken. Failed to read instance profile\ content based on path from the TREXDaemon command line" | warn return "x) Instance profile parsing" | info sapIniParser = sap_discoverer.IniParser() instanceProfileParser = sap_discoverer.InstanceProfileParser( sapIniParser) defaultProfileParser = sap_trex_discoverer.DefaultProfileParser( sapIniParser) try: resultAsIni = instanceProfileParser.parseAsIniResult( profileFile.content) instanceProfile = instanceProfileParser.parse(resultAsIni) defaultProfile = defaultProfileParser.parse(resultAsIni) except Exception: logger.warnException("Failed to parse instance profile") return rfcConfigs = [] trexSystem = defaultProfile.getSystem() trexInstance = instanceProfile.getInstance() trexInstanceName = trexInstance.getName() + trexInstance.getNumber() isBiaProduct = 0 versionInfo = None # # master by default, if topology file is not found that means # # that current one is the only instance # isMaster = 1 trexTopology = None "x) Initialize TREX instance layout" | debug systemName = trexSystem.getName() systemBasePath = sap_discoverer.findSystemBasePath( mainProcess.getExecutablePath(), systemName) if systemBasePath: systemLayout = sap_trex_discoverer.SystemLayout( pathtools, systemBasePath, systemName) 'System path: %s' % systemLayout.getRootPath() | info instancePath = systemLayout.composeInstanceDirPath( trexInstanceName) 'Instance path: %s' % instancePath | debug instanceLayout = sap_trex_discoverer.InstanceLayout( pathtools, instancePath, trexInstanceName) "x) Get content of default profile as it contains information about product" "x) Determine whether we deal with BIA based on version information" | debug defaultProfilePath = systemLayout.getDefaultProfileFilePath() defaultProfileFile = getFileContent(defaultProfilePath) try: resultAsIni = instanceProfileParser.parseAsIniResult( defaultProfileFile.content) defaultProfile = defaultProfileParser.parse(resultAsIni) except Exception: logger.warnException("Failed to parse default profile") else: isBiaProduct = defaultProfile.getProductType( ) == sap_trex.Product.BIA (isBiaProduct and "BIA" or "non-BIA", "product detected") | info # get instance host name from profile name instanceHostname = None try: destinationSystem = sap_discoverer.parseSapSystemFromInstanceProfileName( profileFile.getName()) except Exception: msg = "Failed to parse instance hostname from profile file name" logger.debugException(msg) else: instanceHostname = first( destinationSystem.getInstances()).getHostname() "x) Discover whole topology from (topology.ini)" | info # topology.ini file location and format differs depending on the # product: # -a) BIA (has plain-ini file at <SID>/sys/global/trex/data/topology.ini # -b) TREX (has several places where topology.ini can be stored) discoverTopologyIniFilePath = fptools.safeFunc( sap_trex_discoverer.discoverTopologyIniFilePath) topologyFilePath = (isBiaProduct and systemLayout.getTopologyIniPath() or discoverTopologyIniFilePath( fs, instanceLayout, instanceHostname)) topologyFile = topologyFilePath and getFileContent( topologyFilePath) if topologyFile: try: configParser = sap_trex_discoverer.TopologyConfigParser() trexTopology = sap_trex_discoverer.TrexTopologyConfig( configParser.parse(topologyFile.content)) # find instance between master end-points # landscapeSnapshot = topology.getGlobals().getLandscapeSnapshot() # masterEndpoints = landscapeSnapshot.getMasterEndpoints() # activeMasterEndpoints = landscapeSnapshot.getActiveMasterEndpoints() # topologyNodes = topology.getHostNodes() ## # isEndpointWithInstanceHostname = (lambda # ep, hostname = instanceHostname: ep.getAddress() == hostname) # isMaster = len(filter(isEndpointWithInstanceHostname, # landscapeSnapshot.getMasterEndpoints())) # "host role is %s" % (isMaster and "master" or "slave") | info except: logger.warnException( "Failed to parse topology configuration") else: logger.warn( "Failed to get content for the topology configuration") "x) Discover TREX version information from saptrexmanifest.mf" | info # read version info from manifest file manifestFile = getFileContent(instanceLayout.getManifestFilePath()) if manifestFile: manifestParser = sap_trex_discoverer.SapTrexManifestParser( sapIniParser) versionInfo = manifestParser.parseVersion(manifestFile.content) else: 'Failed to discover version from manifest file' | warn 'Second attept to get version from updateConfig.ini file' | info profileSystem = Sfn( sap_discoverer.parseSapSystemFromInstanceProfileName)( profileFile.getName()) if profileSystem: hostname = first( profileSystem.getInstances()).getHostname() updateConfigFile = getFileContent( instanceLayout.composeUpdateConfigIniFilePath( hostname)) versionInfo = updateConfigFile and sap.VersionInfo( updateConfigFile.content.strip()) "x) Discover served systems ( in case if RFC configuration established )" | info rfcServerIniFilePath = ( isBiaProduct and systemLayout.getRfcServerConfigFilePath() or instanceLayout.composeTrexRfcServerIniFilePath( instanceHostname)) rfcServerIniFile = getFileContent(rfcServerIniFilePath) if rfcServerIniFile: rfcConfigs = filter(None, (fptools.safeFunc( sap_trex_discoverer.parseConnectionsInRfcServerIni)( rfcServerIniFile.content))) # -------------------------------------------------------- REPORTING "SAP TREX plug-in REPORTING start" | info trexOsh = application.getOsh() vector = context.resultsVector configFileReporter = file_topology.Reporter(file_topology.Builder()) trexReporter = sap_trex.Reporter(sap_trex.Builder()) linkReporter = sap.LinkReporter() softwareBuilder = sap.SoftwareBuilder() "x) - report profile content as configuration document for the application" | info vector.add(configFileReporter.report(profileFile, trexOsh)) ("x) - report %s" % trexSystem) | info trexSystemOsh = trexReporter.reportSystem(trexSystem) vector.add(trexSystemOsh) vector.add(linkReporter.reportMembership(trexSystemOsh, trexOsh)) "x) - report instance name and version" | info softwareBuilder.updateName(trexOsh, trexInstanceName) "x) report instance number: %s" % trexInstance.getNumber() | info instanceBuilder = sap_trex.Builder() instanceBuilder.updateInstanceNumber(trexOsh, trexInstance.getNumber()) if versionInfo: softwareBuilder.updateVersionInfo(trexOsh, versionInfo) if isBiaProduct: softwareBuilder.updateDiscoveredProductName( trexOsh, sap_trex.Product.BIA.instanceProductName) "x) report RFC connections" | info dnsResolver = netutils.DnsResolverByShell(shell, destinationIp) vector.addAll(reportRfcConfigs(rfcConfigs, dnsResolver, hostOsh)) "x) report all topology nodes" | info if trexTopology: reportHostNode = fptools.partiallyApply(reportTrexHostNode, fptools._, trexTopology, isBiaProduct) vectors = map(reportHostNode, trexTopology.getHostNodes()) fptools.each(vector.addAll, vectors)