def _reinitScratch(self, fnLog, fUseTheForce): """ Wipes the scratch directories and re-initializes them. No exceptions raise, returns success indicator instead. """ if fUseTheForce is None: fUseTheForce = self._fFirstSignOn class ErrorCallback(object): # pylint: disable=R0903 """ Callbacks + state for the cleanup. """ def __init__(self): self.fRc = True def onErrorCallback(self, sFnName, sPath, aXcptInfo): """ Logs error during shutil.rmtree operation. """ fnLog('Error removing "%s": fn=%s %s' % (sPath, sFnName, aXcptInfo[1])) self.fRc = False oRc = ErrorCallback() # # Cleanup. # for sName in os.listdir(self._oOptions.sScratchRoot): sFullName = os.path.join(self._oOptions.sScratchRoot, sName) try: if os.path.isdir(sFullName): shutil.rmtree(sFullName, False, oRc.onErrorCallback) else: os.remove(sFullName) if os.path.exists(sFullName): raise Exception('Still exists after deletion, weird.') except Exception as oXcpt: if fUseTheForce is True \ and utils.getHostOs() not in ['win', 'os2'] \ and len(sFullName) >= 8 \ and sFullName[0] == '/' \ and sFullName[1] != '/' \ and sFullName.find('/../') < 0: fnLog('Problems deleting "%s" (%s) using the force...' % (sFullName, oXcpt)) try: if os.path.isdir(sFullName): iRc = utils.sudoProcessCall( ['/bin/rm', '-Rf', sFullName]) else: iRc = utils.sudoProcessCall( ['/bin/rm', '-f', sFullName]) if iRc != 0: raise Exception('exit code %s' % iRc) if os.path.exists(sFullName): raise Exception( 'Still exists after forced deletion, weird^2.') except: fnLog('Error sudo deleting "%s": %s' % (sFullName, oXcpt)) oRc.fRc = False else: fnLog('Error deleting "%s": %s' % (sFullName, oXcpt)) oRc.fRc = False # Display files left behind. def dirEnumCallback(sName, oStat): """ callback for dirEnumerateTree """ fnLog(u'%s %s' % (utils.formatFileStat(oStat) if oStat is not None else '????????????', sName)) utils.dirEnumerateTree(self._oOptions.sScratchRoot, dirEnumCallback) # # Re-create the directories. # for sDir in [ self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState ]: if not os.path.isdir(sDir): try: os.makedirs(sDir, 0o700) except Exception as oXcpt: fnLog('Error creating "%s": %s' % (sDir, oXcpt)) oRc.fRc = False if oRc.fRc is True: self._cReinitScratchErrors = 0 else: self._cReinitScratchErrors += 1 return oRc.fRc
class TestBoxScript(object): """ Implementation of the test box script. Communicate with test manager and perform offered actions. """ ## @name Class Constants. # @{ # Scratch space round value (MB). kcMbScratchSpaceRounding = 64 # Memory size round value (MB). kcMbMemoryRounding = 4 # A NULL UUID in string form. ksNullUuid = '00000000-0000-0000-0000-000000000000'; # The minimum dispatch loop delay. kcSecMinDelay = 12; # The maximum dispatch loop delay (inclusive). kcSecMaxDelay = 24; # The minimum sign-on delay. kcSecMinSignOnDelay = 30; # The maximum sign-on delay (inclusive). kcSecMaxSignOnDelay = 60; # Keys for config params VALUE = 'value' FN = 'fn' # pylint: disable=C0103 ## @} def __init__(self, oOptions): """ Initialize internals """ self._oOptions = oOptions; self._sTestBoxHelper = None; # Signed-on state self._cSignOnAttempts = 0; self._fSignedOn = False; self._fNeedReSignOn = False; self._fFirstSignOn = True; self._idTestBox = None; self._sTestBoxName = ''; self._sTestBoxUuid = self.ksNullUuid; # convenience, assigned below. # Command processor. self._oCommand = TestBoxCommand(self); # # Scratch dir setup. Use /var/tmp instead of /tmp because we may need # many many GBs for some test scenarios and /tmp can be backed by swap # or be a fast+small disk of some kind, while /var/tmp is normally # larger, if slower. /var/tmp is generally not cleaned up on reboot, # /tmp often is, this would break host panic / triple-fault detection. # if self._oOptions.sScratchRoot is None: if utils.getHostOs() in ('win', 'os2', 'haiku', 'dos'): # We need *lots* of space, so avoid /tmp as it may be a memory # file system backed by the swap file, or worse. self._oOptions.sScratchRoot = tempfile.gettempdir(); else: self._oOptions.sScratchRoot = '/var/tmp'; sSubDir = 'testbox'; try: sSubDir = '%s-%u' % (sSubDir, os.getuid()); # pylint: disable=E1101 except: pass; self._oOptions.sScratchRoot = os.path.join(self._oOptions.sScratchRoot, sSubDir); self._sScratchSpill = os.path.join(self._oOptions.sScratchRoot, 'scratch'); self._sScratchScripts = os.path.join(self._oOptions.sScratchRoot, 'scripts'); self._sScratchState = os.path.join(self._oOptions.sScratchRoot, 'state'); # persistant storage. for sDir in [self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState]: if not os.path.isdir(sDir): os.makedirs(sDir, 0700); # We count consecutive reinitScratch failures and will reboot the # testbox after a while in the hope that it will correct the issue. self._cReinitScratchErrors = 0; # # Mount builds and test resources if requested. # self.mountShares(); # # Sign-on parameters: Packed into list of records of format: # { <Parameter ID>: { <Current value>, <Check function> } } # self._ddSignOnParams = \ { constants.tbreq.ALL_PARAM_TESTBOX_UUID: { self.VALUE: self._getHostSystemUuid(), self.FN: None }, constants.tbreq.SIGNON_PARAM_OS: { self.VALUE: utils.getHostOs(), self.FN: None }, constants.tbreq.SIGNON_PARAM_OS_VERSION: { self.VALUE: utils.getHostOsVersion(), self.FN: None }, constants.tbreq.SIGNON_PARAM_CPU_ARCH: { self.VALUE: utils.getHostArch(), self.FN: None }, constants.tbreq.SIGNON_PARAM_CPU_VENDOR: { self.VALUE: self._getHostCpuVendor(), self.FN: None }, constants.tbreq.SIGNON_PARAM_CPU_NAME: { self.VALUE: self._getHostCpuName(), self.FN: None }, constants.tbreq.SIGNON_PARAM_CPU_REVISION: { self.VALUE: self._getHostCpuRevision(), self.FN: None }, constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT: { self.VALUE: self._hasHostHwVirt(), self.FN: None }, constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING:{ self.VALUE: self._hasHostNestedPaging(), self.FN: None }, constants.tbreq.SIGNON_PARAM_HAS_64_BIT_GUEST: { self.VALUE: self._can64BitGuest(), self.FN: None }, constants.tbreq.SIGNON_PARAM_HAS_IOMMU: { self.VALUE: self._hasHostIoMmu(), self.FN: None }, #constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE: { self.VALUE: self._withRawModeSupport(), self.FN: None }, constants.tbreq.SIGNON_PARAM_SCRIPT_REV: { self.VALUE: self._getScriptRev(), self.FN: None }, constants.tbreq.SIGNON_PARAM_REPORT: { self.VALUE: self._getHostReport(), self.FN: None }, constants.tbreq.SIGNON_PARAM_PYTHON_VERSION: { self.VALUE: self._getPythonHexVersion(), self.FN: None }, constants.tbreq.SIGNON_PARAM_CPU_COUNT: { self.VALUE: None, self.FN: multiprocessing.cpu_count }, constants.tbreq.SIGNON_PARAM_MEM_SIZE: { self.VALUE: None, self.FN: self._getHostMemSize }, constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE: { self.VALUE: None, self.FN: self._getFreeScratchSpace }, } for sItem in self._ddSignOnParams: if self._ddSignOnParams[sItem][self.FN] is not None: self._ddSignOnParams[sItem][self.VALUE] = self._ddSignOnParams[sItem][self.FN]() testboxcommons.log('Starting Test Box script (%s)' % (self._getScriptRev(),)); testboxcommons.log('Test Manager URL: %s' % self._oOptions.sTestManagerUrl,) testboxcommons.log('Scratch root path: %s' % self._oOptions.sScratchRoot,) for sItem in self._ddSignOnParams: testboxcommons.log('Sign-On value %18s: %s' % (sItem, self._ddSignOnParams[sItem][self.VALUE])); # # The System UUID is the primary identification of the machine, so # refuse to cooperate if it's NULL. # self._sTestBoxUuid = self.getSignOnParam(constants.tbreq.ALL_PARAM_TESTBOX_UUID); if self._sTestBoxUuid == self.ksNullUuid: raise TestBoxScriptException('Couldn\'t determine the System UUID, please use --system-uuid to specify it.'); # # Export environment variables, clearing any we don't know yet. # for sEnvVar in self._oOptions.asEnvVars: iEqual = sEnvVar.find('='); if iEqual == -1: # No '=', remove it. if sEnvVar in os.environ: del os.environ[sEnvVar]; elif iEqual > 0: # Set it. os.environ[sEnvVar[:iEqual]] = sEnvVar[iEqual+1:]; else: # Starts with '=', bad user. raise TestBoxScriptException('Invalid -E argument: "%s"' % (sEnvVar,)); os.environ['TESTBOX_PATH_BUILDS'] = self._oOptions.sBuildsPath; os.environ['TESTBOX_PATH_RESOURCES'] = self._oOptions.sTestRsrcPath; os.environ['TESTBOX_PATH_SCRATCH'] = self._sScratchSpill; os.environ['TESTBOX_PATH_SCRIPTS'] = self._sScratchScripts; os.environ['TESTBOX_PATH_UPLOAD'] = self._sScratchSpill; ## @todo drop the UPLOAD dir? os.environ['TESTBOX_HAS_HW_VIRT'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_HW_VIRT); os.environ['TESTBOX_HAS_NESTED_PAGING'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_NESTED_PAGING); os.environ['TESTBOX_HAS_IOMMU'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_HAS_IOMMU); os.environ['TESTBOX_SCRIPT_REV'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_SCRIPT_REV); os.environ['TESTBOX_CPU_COUNT'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_CPU_COUNT); os.environ['TESTBOX_MEM_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_MEM_SIZE); os.environ['TESTBOX_SCRATCH_SIZE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_SCRATCH_SIZE); #TODO: os.environ['TESTBOX_WITH_RAW_MODE'] = self.getSignOnParam(constants.tbreq.SIGNON_PARAM_WITH_RAW_MODE); os.environ['TESTBOX_WITH_RAW_MODE'] = str(self._withRawModeSupport()); os.environ['TESTBOX_MANAGER_URL'] = self._oOptions.sTestManagerUrl; os.environ['TESTBOX_UUID'] = self._sTestBoxUuid; os.environ['TESTBOX_REPORTER'] = 'remote'; os.environ['TESTBOX_NAME'] = ''; os.environ['TESTBOX_ID'] = ''; os.environ['TESTBOX_TEST_SET_ID'] = ''; os.environ['TESTBOX_TIMEOUT'] = '0'; os.environ['TESTBOX_TIMEOUT_ABS'] = '0'; if utils.getHostOs() == 'win': os.environ['COMSPEC'] = os.path.join(os.environ['SystemRoot'], 'System32', 'cmd.exe'); # Currently omitting any kBuild tools. def mountShares(self): """ Mounts the shares. Raises exception on failure. """ self._mountShare(self._oOptions.sBuildsPath, self._oOptions.sBuildsServerType, self._oOptions.sBuildsServerName, self._oOptions.sBuildsServerShare, self._oOptions.sBuildsServerUser, self._oOptions.sBuildsServerPasswd, 'builds'); self._mountShare(self._oOptions.sTestRsrcPath, self._oOptions.sTestRsrcServerType, self._oOptions.sTestRsrcServerName, self._oOptions.sTestRsrcServerShare, self._oOptions.sTestRsrcServerUser, self._oOptions.sTestRsrcServerPasswd, 'testrsrc'); return True; def _mountShare(self, sMountPoint, sType, sServer, sShare, sUser, sPassword, sWhat): """ Mounts the specified share if needed. Raises exception on failure. """ # Only mount if the type is specified. if sType is None: return True; # Test if already mounted. sTestFile = os.path.join(sMountPoint + os.path.sep, sShare + '-new.txt'); if os.path.isfile(sTestFile): return True; # # Platform specific mount code. # sHostOs = utils.getHostOs() if sHostOs in ('darwin', 'freebsd'): utils.sudoProcessCall(['/sbin/umount', sMountPoint]); utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]); utils.sudoProcessCall(['/usr/sbin/chown', str(os.getuid()), sMountPoint]); # pylint: disable=E1101 if sType == 'cifs': # Note! no smb://server/share stuff here, 10.6.8 didn't like it. utils.processOutputChecked(['/sbin/mount_smbfs', '-o', 'automounted,nostreams,soft,noowners,noatime,rdonly', '-f', '0555', '-d', '0555', '//%s:%s@%s/%s' % (sUser, sPassword, sServer, sShare), sMountPoint]); else: raise TestBoxScriptException('Unsupported server type %s.' % (sType,)); elif sHostOs == 'linux': utils.sudoProcessCall(['/bin/umount', sMountPoint]); utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]); if sType == 'cifs': utils.sudoProcessOutputChecked(['/bin/mount', '-t', 'cifs', '-o', 'user='******',password='******',sec=ntlmv2' + ',uid=' + str(os.getuid()) # pylint: disable=E1101 + ',gid=' + str(os.getgid()) # pylint: disable=E1101 + ',nounix,file_mode=0555,dir_mode=0555,soft,ro', '//%s/%s' % (sServer, sShare), sMountPoint]); elif sType == 'nfs': utils.sudoProcessOutputChecked(['/bin/mount', '-t', 'nfs', '-o', 'soft,ro', '%s:%s' % (sServer, sShare if sShare.find('/') >= 0 else ('/export/' + sShare)), sMountPoint]); else: raise TestBoxScriptException('Unsupported server type %s.' % (sType,)); elif sHostOs == 'solaris': utils.sudoProcessCall(['/sbin/umount', sMountPoint]); utils.sudoProcessCall(['/bin/mkdir', '-p', sMountPoint]); if sType == 'cifs': ## @todo This stuff doesn't work on wei01-x4600b.de.oracle.com running 11.1. FIXME! oPasswdFile = tempfile.TemporaryFile(); oPasswdFile.write(sPassword + '\n'); oPasswdFile.flush(); utils.sudoProcessOutputChecked(['/sbin/mount', '-F', 'smbfs', '-o', 'user='******',uid=' + str(os.getuid()) # pylint: disable=E1101 + ',gid=' + str(os.getgid()) # pylint: disable=E1101 + ',fileperms=0555,dirperms=0555,noxattr,ro', '//%s/%s' % (sServer, sShare), sMountPoint], stdin = oPasswdFile); oPasswdFile.close(); elif sType == 'nfs': utils.sudoProcessOutputChecked(['/sbin/mount', '-F', 'nfs', '-o', 'noxattr,ro', '%s:%s' % (sServer, sShare if sShare.find('/') >= 0 else ('/export/' + sShare)), sMountPoint]); else: raise TestBoxScriptException('Unsupported server type %s.' % (sType,)); elif sHostOs == 'win': if sType != 'cifs': raise TestBoxScriptException('Only CIFS mounts are supported on Windows.'); utils.processCall(['net', 'use', sMountPoint, '/d']); utils.processOutputChecked(['net', 'use', sMountPoint, '\\\\' + sServer + '\\' + sShare, sPassword, '/USER:'******'Unsupported host %s' % (sHostOs,)); # # Re-test. # if not os.path.isfile(sTestFile): raise TestBoxException('Failed to mount %s (%s[%s]) at %s: %s not found' % (sWhat, sServer, sShare, sMountPoint, sTestFile)); return True; ## @name Signon property releated methods. # @{ def _getHelperOutput(self, sCmd): """ Invokes TestBoxHelper to obtain information hard to access from python. """ if self._sTestBoxHelper is None: if not utils.isRunningFromCheckout(): # See VBoxTestBoxScript.zip for layout. self._sTestBoxHelper = os.path.join(g_ksValidationKitDir, utils.getHostOs(), utils.getHostArch(), \ 'TestBoxHelper'); else: # Only for in-tree testing, so don't bother be too accurate right now. sType = os.environ.get('KBUILD_TYPE', os.environ.get('BUILD_TYPE', 'debug')); self._sTestBoxHelper = os.path.join(g_ksValidationKitDir, os.pardir, os.pardir, os.pardir, 'out', \ utils.getHostOsDotArch(), sType, 'testboxscript', \ utils.getHostOs(), utils.getHostArch(), \ 'TestBoxHelper'); if utils.getHostOs() in ['win', 'os2']: self._sTestBoxHelper += '.exe'; return utils.processOutputChecked([self._sTestBoxHelper, sCmd]).strip(); def _getHelperOutputTristate(self, sCmd, fDunnoValue): """ Invokes TestBoxHelper to obtain information hard to access from python. """ sValue = self._getHelperOutput(sCmd); sValue = sValue.lower(); if sValue == 'true': return True; if sValue == 'false': return False; if sValue != 'dunno' and sValue != 'none': raise TestBoxException('Unexpected response "%s" to helper command "%s"' % (sValue, sCmd)); return fDunnoValue; @staticmethod def _isUuidGood(sUuid): """ Checks if the UUID looks good. There are systems with really bad UUIDs, for instance "03000200-0400-0500-0006-000700080009". """ if sUuid == TestBoxScript.ksNullUuid: return False; sUuid = sUuid.lower(); for sDigit in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']: if sUuid.count(sDigit) > 16: return False; return True; def _getHostSystemUuid(self): """ Get the system UUID string from the System, return null-uuid if unable to get retrieve it. """ if self._oOptions.sSystemUuid is not None: return self._oOptions.sSystemUuid; sUuid = self.ksNullUuid; # # Try get at the firmware UUID. # if utils.getHostOs() == 'linux': # NOTE: This requires to have kernel option enabled: # Firmware Drivers -> Export DMI identification via sysfs to userspace if os.path.exists('/sys/devices/virtual/dmi/id/product_uuid'): try: sVar = utils.sudoProcessOutputChecked(['cat', '/sys/devices/virtual/dmi/id/product_uuid']); sUuid = str(uuid.UUID(sVar.strip())); except: pass; ## @todo consider dmidecoder? What about EFI systems? elif utils.getHostOs() == 'win': # Windows: WMI try: import win32com.client; # pylint: disable=F0401 oWmi = win32com.client.Dispatch('WbemScripting.SWbemLocator'); oWebm = oWmi.ConnectServer('.', 'root\\cimv2'); for oItem in oWebm.ExecQuery('SELECT * FROM Win32_ComputerSystemProduct'): if oItem.UUID != None: sUuid = str(uuid.UUID(oItem.UUID)); except: pass; elif utils.getHostOs() == 'darwin': try: sVar = utils.processOutputChecked(['/bin/sh', '-c', '/usr/sbin/ioreg -k IOPlatformUUID' \ + '| /usr/bin/grep IOPlatformUUID' \ + '| /usr/bin/head -1']); sVar = sVar.strip()[-(len(self.ksNullUuid) + 1):-1]; sUuid = str(uuid.UUID(sVar)); except: pass; elif utils.getHostOs() == 'solaris': # Solaris: The smbios util. try: sVar = utils.processOutputChecked(['/bin/sh', '-c', '/usr/sbin/smbios ' \ + '| /usr/xpg4/bin/sed -ne \'s/^.*UUID: *//p\'' \ + '| /usr/bin/head -1']); sUuid = str(uuid.UUID(sVar.strip())); except: pass; if self._isUuidGood(sUuid): return sUuid; # # Try add the MAC address. # uuid.getnode may provide it, or it may return a random number... # lMacAddr = uuid.getnode(); sNode = '%012x' % (lMacAddr,) if lMacAddr == uuid.getnode() and lMacAddr != 0 and len(sNode) == 12: return sUuid[:-12] + sNode; return sUuid; def _getHostCpuVendor(self): """ Get the CPUID vendor string on intel HW. """ return self._getHelperOutput('cpuvendor'); def _getHostCpuName(self): """ Get the CPU name/description string. """ return self._getHelperOutput('cpuname'); def _getHostCpuRevision(self): """ Get the CPU revision (family/model/stepping) value. """ return self._getHelperOutput('cpurevision'); def _hasHostHwVirt(self): """ Check if the host supports AMD-V or VT-x """ if self._oOptions.fHasHwVirt is None: self._oOptions.fHasHwVirt = self._getHelperOutput('cpuhwvirt'); return self._oOptions.fHasHwVirt; def _hasHostNestedPaging(self): """ Check if the host supports nested paging. """ if not self._hasHostHwVirt(): return False; if self._oOptions.fHasNestedPaging is None: self._oOptions.fHasNestedPaging = self._getHelperOutputTristate('nestedpaging', False); return self._oOptions.fHasNestedPaging; def _can64BitGuest(self): """ Check if the we (VBox) can run 64-bit guests. """ if not self._hasHostHwVirt(): return False; if self._oOptions.fCan64BitGuest is None: self._oOptions.fCan64BitGuest = self._getHelperOutputTristate('longmode', True); return self._oOptions.fCan64BitGuest; def _hasHostIoMmu(self): """ Check if the host has an I/O MMU of the VT-d kind. """ if not self._hasHostHwVirt(): return False; if self._oOptions.fHasIoMmu is None: ## @todo Any way to figure this one out on any host OS? self._oOptions.fHasIoMmu = False; return self._oOptions.fHasIoMmu; def _withRawModeSupport(self): """ Check if the testbox is configured with raw-mode support or not. """ if self._oOptions.fWithRawMode is None: self._oOptions.fWithRawMode = True; return self._oOptions.fWithRawMode; def _getHostReport(self): """ Generate a report about the host hardware and software. """ return self._getHelperOutput('report'); def _getHostMemSize(self): """ Gets the amount of physical memory on the host (and accessible to the OS, i.e. don't report stuff over 4GB if Windows doesn't wanna use it). Unit: MiB. """ cMbMemory = long(self._getHelperOutput('memsize').strip()) / (1024 * 1024); # Round it. cMbMemory = long(math.floor(cMbMemory / self.kcMbMemoryRounding)) * self.kcMbMemoryRounding; return cMbMemory; def _getFreeScratchSpace(self): """ Get free space on the volume where scratch directory is located and return it in bytes rounded down to nearest 64MB (currently works on Linux only) Unit: MiB. """ if platform.system() == 'Windows': import ctypes cTypeMbFreeSpace = ctypes.c_ulonglong(0) ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(self._oOptions.sScratchRoot), None, None, ctypes.pointer(cTypeMbFreeSpace)) cMbFreeSpace = cTypeMbFreeSpace.value else: stats = os.statvfs(self._oOptions.sScratchRoot); # pylint: disable=E1101 cMbFreeSpace = stats.f_frsize * stats.f_bfree # Convert to MB cMbFreeSpace = long(cMbFreeSpace) /(1024 * 1024) # Round free space size cMbFreeSpace = long(math.floor(cMbFreeSpace / self.kcMbScratchSpaceRounding)) * self.kcMbScratchSpaceRounding; return cMbFreeSpace; def _getScriptRev(self): """ The script (subversion) revision number. """ sRev = '@VBOX_SVN_REV@'; sRev = sRev.strip(); # just in case... try: _ = int(sRev); except: return __version__[11:-1].strip(); return sRev; def _getPythonHexVersion(self): """ The python hex version number. """ uHexVersion = getattr(sys, 'hexversion', None); if uHexVersion is None: uHexVersion = (sys.version_info[0] << 24) | (sys.version_info[1] << 16) | (sys.version_info[2] << 8); if sys.version_info[3] == 'final': uHexVersion |= 0xf0; return uHexVersion; # @} def openTestManagerConnection(self): """ Opens up a connection to the test manager. Raises exception on failure. """ return TestBoxConnection(self._oOptions.sTestManagerUrl, self._idTestBox, self._sTestBoxUuid); def getSignOnParam(self, sName): """ Returns a sign-on parameter value as string. Raises exception if the name is incorrect. """ return str(self._ddSignOnParams[sName][self.VALUE]); def getPathState(self): """ Get the path to the state dir in the scratch area. """ return self._sScratchState; def getPathScripts(self): """ Get the path to the scripts dir (TESTBOX_PATH_SCRIPTS) in the scratch area. """ return self._sScratchScripts; def getPathSpill(self): """ Get the path to the spill dir (TESTBOX_PATH_SCRATCH) in the scratch area. """ return self._sScratchSpill; def getPathBuilds(self): """ Get the path to the builds. """ return self._oOptions.sBuildsPath; def getTestBoxId(self): """ Get the TestBox ID for state saving purposes. """ return self._idTestBox; def getTestBoxName(self): """ Get the TestBox name for state saving purposes. """ return self._sTestBoxName; def _reinitScratch(self, fnLog, fUseTheForce): """ Wipes the scratch directories and re-initializes them. No exceptions raise, returns success indicator instead. """ if fUseTheForce is None: fUseTheForce = self._fFirstSignOn; class ErrorCallback(object): # pylint: disable=R0903 """ Callbacks + state for the cleanup. """ def __init__(self): self.fRc = True; def onErrorCallback(self, sFnName, sPath, aXcptInfo): """ Logs error during shutil.rmtree operation. """ fnLog('Error removing "%s": fn=%s %s' % (sPath, sFnName, aXcptInfo[1])); self.fRc = False; oRc = ErrorCallback(); # # Cleanup. # for sName in os.listdir(self._oOptions.sScratchRoot): sFullName = os.path.join(self._oOptions.sScratchRoot, sName); try: if os.path.isdir(sFullName): shutil.rmtree(sFullName, False, oRc.onErrorCallback); else: os.remove(sFullName); if os.path.exists(sFullName): raise Exception('Still exists after deletion, weird.'); except Exception, oXcpt: if fUseTheForce is True \ and utils.getHostOs() not in ['win', 'os2'] \ and len(sFullName) >= 8 \ and sFullName[0] == '/' \ and sFullName[1] != '/' \ and sFullName.find('/../') < 0: fnLog('Problems deleting "%s" (%s) using the force...' % (sFullName, oXcpt)); try: if os.path.isdir(sFullName): iRc = utils.sudoProcessCall(['/bin/rm', '-Rf', sFullName]) else: iRc = utils.sudoProcessCall(['/bin/rm', '-f', sFullName]) if iRc != 0: raise Exception('exit code %s' % iRc); if os.path.exists(sFullName): raise Exception('Still exists after forced deletion, weird^2.'); except: fnLog('Error sudo deleting "%s": %s' % (sFullName, oXcpt)); oRc.fRc = False; else: fnLog('Error deleting "%s": %s' % (sFullName, oXcpt)); oRc.fRc = False; # Display files left behind. def dirEnumCallback(sName, oStat): """ callback for dirEnumerateTree """ fnLog(u'%s %s' % (utils.formatFileStat(oStat) if oStat is not None else '????????????', sName)); utils.dirEnumerateTree(self._oOptions.sScratchRoot, dirEnumCallback); # # Re-create the directories. # for sDir in [self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState]: if not os.path.isdir(sDir): try: os.makedirs(sDir, 0700); except Exception, oXcpt: fnLog('Error creating "%s": %s' % (sDir, oXcpt)); oRc.fRc = False;
def _reinitScratch(self, fnLog, fUseTheForce): """ Wipes the scratch directories and re-initializes them. No exceptions raise, returns success indicator instead. """ if fUseTheForce is None: fUseTheForce = self._fFirstSignOn; class ErrorCallback(object): # pylint: disable=R0903 """ Callbacks + state for the cleanup. """ def __init__(self): self.fRc = True; def onErrorCallback(self, sFnName, sPath, aXcptInfo): """ Logs error during shutil.rmtree operation. """ fnLog('Error removing "%s": fn=%s %s' % (sPath, sFnName, aXcptInfo[1])); self.fRc = False; oRc = ErrorCallback(); # # Cleanup. # for sName in os.listdir(self._oOptions.sScratchRoot): sFullName = os.path.join(self._oOptions.sScratchRoot, sName); try: if os.path.isdir(sFullName): shutil.rmtree(sFullName, False, oRc.onErrorCallback); else: os.remove(sFullName); if os.path.exists(sFullName): raise Exception('Still exists after deletion, weird.'); except Exception as oXcpt: if fUseTheForce is True \ and utils.getHostOs() not in ['win', 'os2'] \ and len(sFullName) >= 8 \ and sFullName[0] == '/' \ and sFullName[1] != '/' \ and sFullName.find('/../') < 0: fnLog('Problems deleting "%s" (%s) using the force...' % (sFullName, oXcpt)); try: if os.path.isdir(sFullName): iRc = utils.sudoProcessCall(['/bin/rm', '-Rf', sFullName]) else: iRc = utils.sudoProcessCall(['/bin/rm', '-f', sFullName]) if iRc != 0: raise Exception('exit code %s' % iRc); if os.path.exists(sFullName): raise Exception('Still exists after forced deletion, weird^2.'); except: fnLog('Error sudo deleting "%s": %s' % (sFullName, oXcpt)); oRc.fRc = False; else: fnLog('Error deleting "%s": %s' % (sFullName, oXcpt)); oRc.fRc = False; # Display files left behind. def dirEnumCallback(sName, oStat): """ callback for dirEnumerateTree """ fnLog(u'%s %s' % (utils.formatFileStat(oStat) if oStat is not None else '????????????', sName)); utils.dirEnumerateTree(self._oOptions.sScratchRoot, dirEnumCallback); # # Re-create the directories. # for sDir in [self._oOptions.sScratchRoot, self._sScratchSpill, self._sScratchScripts, self._sScratchState]: if not os.path.isdir(sDir): try: os.makedirs(sDir, 0o700); except Exception as oXcpt: fnLog('Error creating "%s": %s' % (sDir, oXcpt)); oRc.fRc = False; if oRc.fRc is True: self._cReinitScratchErrors = 0; else: self._cReinitScratchErrors += 1; return oRc.fRc;