testbox.py revision cf22150eaeeb72431bf1cf65c309a431454fb22b
20593760b116c90f3e439552763eef632a3bbb17vboxsync# -*- coding: utf-8 -*-
20593760b116c90f3e439552763eef632a3bbb17vboxsync# $Id$
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync"""
20593760b116c90f3e439552763eef632a3bbb17vboxsyncTest Manager - TestBox.
20593760b116c90f3e439552763eef632a3bbb17vboxsync"""
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync__copyright__ = \
20593760b116c90f3e439552763eef632a3bbb17vboxsync"""
20593760b116c90f3e439552763eef632a3bbb17vboxsyncCopyright (C) 2012-2014 Oracle Corporation
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsyncThis file is part of VirtualBox Open Source Edition (OSE), as
20593760b116c90f3e439552763eef632a3bbb17vboxsyncavailable from http://www.virtualbox.org. This file is free software;
20593760b116c90f3e439552763eef632a3bbb17vboxsyncyou can redistribute it and/or modify it under the terms of the GNU
20593760b116c90f3e439552763eef632a3bbb17vboxsyncGeneral Public License (GPL) as published by the Free Software
20593760b116c90f3e439552763eef632a3bbb17vboxsyncFoundation, in version 2 as it comes in the "COPYING" file of the
20593760b116c90f3e439552763eef632a3bbb17vboxsyncVirtualBox OSE distribution. VirtualBox OSE is distributed in the
20593760b116c90f3e439552763eef632a3bbb17vboxsynchope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsyncThe contents of this file may alternatively be used under the terms
20593760b116c90f3e439552763eef632a3bbb17vboxsyncof the Common Development and Distribution License Version 1.0
20593760b116c90f3e439552763eef632a3bbb17vboxsync(CDDL) only, as it comes in the "COPYING.CDDL" file of the
20593760b116c90f3e439552763eef632a3bbb17vboxsyncVirtualBox OSE distribution, in which case the provisions of the
20593760b116c90f3e439552763eef632a3bbb17vboxsyncCDDL are applicable instead of those of the GPL.
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsyncYou may elect to license modified versions of this file under the
20593760b116c90f3e439552763eef632a3bbb17vboxsyncterms and conditions of either the GPL or the CDDL or both.
20593760b116c90f3e439552763eef632a3bbb17vboxsync"""
20593760b116c90f3e439552763eef632a3bbb17vboxsync__version__ = "$Revision$"
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync# Standard python imports.
20593760b116c90f3e439552763eef632a3bbb17vboxsyncimport unittest;
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync# Validation Kit imports.
20593760b116c90f3e439552763eef632a3bbb17vboxsyncfrom testmanager.core.base import ModelDataBase, ModelDataBaseTestCase, ModelLogicBase, TMExceptionBase, \
20593760b116c90f3e439552763eef632a3bbb17vboxsync ChangeLogEntry, AttributeChangeEntry;
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync# pylint: disable=C0103
ea9fe8748652b3e7b3f1245001f9d7cd8c9eaaccvboxsyncclass TestBoxData(ModelDataBase): # pylint: disable=R0902
20593760b116c90f3e439552763eef632a3bbb17vboxsync """
20593760b116c90f3e439552763eef632a3bbb17vboxsync TestBox Data.
20593760b116c90f3e439552763eef632a3bbb17vboxsync """
0c8eec6f5b79d800c3d25b5c0e0d7ad3f867c25cvboxsync
0c8eec6f5b79d800c3d25b5c0e0d7ad3f867c25cvboxsync ## LomKind_T
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksLomKind_None = 'none';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksLomKind_ILOM = 'ilom';
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync ksLomKind_ELOM = 'elom';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksLomKind_AppleXserveLom = 'apple-xserver-lom';
20593760b116c90f3e439552763eef632a3bbb17vboxsync kasLomKindValues = [ ksLomKind_None, ksLomKind_ILOM, ksLomKind_ELOM, ksLomKind_AppleXserveLom];
20593760b116c90f3e439552763eef632a3bbb17vboxsync kaoLomKindDescs = \
20593760b116c90f3e439552763eef632a3bbb17vboxsync [
ea9fe8748652b3e7b3f1245001f9d7cd8c9eaaccvboxsync ( ksLomKind_None, 'None', ''),
ea9fe8748652b3e7b3f1245001f9d7cd8c9eaaccvboxsync ( ksLomKind_ILOM, 'ILOM', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksLomKind_ELOM, 'ELOM', ''),
ea9fe8748652b3e7b3f1245001f9d7cd8c9eaaccvboxsync ( ksLomKind_AppleXserveLom, 'Apple Xserve LOM', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ];
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync ## TestBoxCmd_T
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_None = 'none';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_Abort = 'abort';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_Reboot = 'reboot';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_Upgrade = 'upgrade';
e86538a7bc028e823f16f8982e90f0c7ef5d4ecevboxsync ksTestBoxCmd_UpgradeAndReboot = 'upgrade-and-reboot';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_Special = 'special';
20593760b116c90f3e439552763eef632a3bbb17vboxsync kasTestBoxCmdValues = [ ksTestBoxCmd_None, ksTestBoxCmd_Abort, ksTestBoxCmd_Reboot, ksTestBoxCmd_Upgrade,
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksTestBoxCmd_UpgradeAndReboot, ksTestBoxCmd_Special];
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync kaoTestBoxCmdDescs = \
20593760b116c90f3e439552763eef632a3bbb17vboxsync [
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksTestBoxCmd_None, 'None', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksTestBoxCmd_Abort, 'Abort current test', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksTestBoxCmd_Reboot, 'Reboot TestBox', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksTestBoxCmd_Upgrade, 'Upgrade TestBox Script', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ( ksTestBoxCmd_UpgradeAndReboot, 'Upgrade TestBox Script and reboot', ''),
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync ( ksTestBoxCmd_Special, 'Special (reserved)', ''),
20593760b116c90f3e439552763eef632a3bbb17vboxsync ];
20593760b116c90f3e439552763eef632a3bbb17vboxsync
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksIdAttr = 'idTestBox';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksIdGenAttr = 'idGenTestBox';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_idTestBox = 'TestBox_idTestBox';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_tsEffective = 'TestBox_tsEffective';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_tsExpire = 'TestBox_tsExpire';
0f7b309cca2790ee47e71b5aa1817803b652f253vboxsync ksParam_uidAuthor = 'TestBox_uidAuthor';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_idGenTestBox = 'TestBox_idGenTestBox';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_ip = 'TestBox_ip';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_uuidSystem = 'TestBox_uuidSystem';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_sName = 'TestBox_sName';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_sDescription = 'TestBox_sDescription';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_idSchedGroup = 'TestBox_idSchedGroup';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_fEnabled = 'TestBox_fEnabled';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_enmLomKind = 'TestBox_enmLomKind';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_ipLom = 'TestBox_ipLom';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_pctScaleTimeout = 'TestBox_pctScaleTimeout';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_sOs = 'TestBox_sOs';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_sOsVersion = 'TestBox_sOsVersion';
e9525bea57dc13d82fd3392913aebb33d2cb79e3vboxsync ksParam_sCpuVendor = 'TestBox_sCpuVendor';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_sCpuArch = 'TestBox_sCpuArch';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_sCpuName = 'TestBox_sCpuName';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_lCpuRevision = 'TestBox_lCpuRevision';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_cCpus = 'TestBox_cCpus';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_fCpuHwVirt = 'TestBox_fCpuHwVirt';
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync ksParam_fCpuNestedPaging = 'TestBox_fCpuNestedPaging';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_fCpu64BitGuest = 'TestBox_fCpu64BitGuest';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_fChipsetIoMmu = 'TestBox_fChipsetIoMmu';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_cMbMemory = 'TestBox_cMbMemory';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_cMbScratch = 'TestBox_cMbScratch';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_sReport = 'TestBox_sReport';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_iTestBoxScriptRev = 'TestBox_iTestBoxScriptRev';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_iPythonHexVersion = 'TestBox_iPythonHexVersion';
20593760b116c90f3e439552763eef632a3bbb17vboxsync ksParam_enmPendingCmd = 'TestBox_enmPendingCmd';
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync
20593760b116c90f3e439552763eef632a3bbb17vboxsync kasAllowNullAttributes = ['idTestBox', 'tsEffective', 'tsExpire', 'uidAuthor', 'idGenTestBox', 'sDescription',
20593760b116c90f3e439552763eef632a3bbb17vboxsync 'ipLom', 'sOs', 'sOsVersion', 'sCpuVendor', 'sCpuArch', 'sCpuName', 'lCpuRevision', 'cCpus',
20593760b116c90f3e439552763eef632a3bbb17vboxsync 'fCpuHwVirt', 'fCpuNestedPaging', 'fCpu64BitGuest', 'fChipsetIoMmu', 'cMbMemory',
20593760b116c90f3e439552763eef632a3bbb17vboxsync 'cMbScratch', 'sReport', 'iTestBoxScriptRev', 'iPythonHexVersion' ];
20593760b116c90f3e439552763eef632a3bbb17vboxsync kasValidValues_enmLomKind = kasLomKindValues;
20593760b116c90f3e439552763eef632a3bbb17vboxsync kasValidValues_enmPendingCmd = kasTestBoxCmdValues;
20593760b116c90f3e439552763eef632a3bbb17vboxsync kiMin_pctScaleTimeout = 11;
20593760b116c90f3e439552763eef632a3bbb17vboxsync kiMax_pctScaleTimeout = 19999;
aa4bcf0a4b2db3ac352b56a291d49cb8d4b66d32vboxsync kcchMax_sReport = 65535;
20593760b116c90f3e439552763eef632a3bbb17vboxsync
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync def __init__(self):
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync ModelDataBase.__init__(self);
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync #
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync # Initialize with defaults.
a18faae7b59910c9f2da2886ac10d7f31e29cd83vboxsync # See the database for explanations of each of these fields.
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync #
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.idTestBox = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.tsEffective = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.tsExpire = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.uidAuthor = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.idGenTestBox = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.ip = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.uuidSystem = None;
e00b7e275dee70ffbd9e00a3e3e3e8513287e2b2vboxsync self.sName = None;
self.sDescription = None;
self.idSchedGroup = 1;
self.fEnabled = False;
self.enmLomKind = self.ksLomKind_None;
self.ipLom = None;
self.pctScaleTimeout = 100;
self.sOs = None;
self.sOsVersion = None;
self.sCpuVendor = None;
self.sCpuArch = None;
self.sCpuName = None;
self.lCpuRevision = None;
self.cCpus = 1;
self.fCpuHwVirt = False;
self.fCpuNestedPaging = False;
self.fCpu64BitGuest = False;
self.fChipsetIoMmu = False;
self.cMbMemory = 1;
self.cMbScratch = 0;
self.sReport = None;
self.iTestBoxScriptRev = 0;
self.iPythonHexVersion = 0;
self.enmPendingCmd = self.ksTestBoxCmd_None;
def initFromDbRow(self, aoRow):
"""
Internal worker for initFromDbWithId and initFromDbWithGenId as well as
from TestBoxLogic. Expecting a SELECT * FROM TestBoxes result.
"""
if aoRow is None:
raise TMExceptionBase('TestBox not found.');
self.idTestBox = aoRow[0];
self.tsEffective = aoRow[1];
self.tsExpire = aoRow[2];
self.uidAuthor = aoRow[3];
self.idGenTestBox = aoRow[4];
self.ip = aoRow[5];
self.uuidSystem = aoRow[6];
self.sName = aoRow[7];
self.sDescription = aoRow[8];
self.idSchedGroup = aoRow[9];
self.fEnabled = aoRow[10];
self.enmLomKind = aoRow[11];
self.ipLom = aoRow[12];
self.pctScaleTimeout = aoRow[13];
self.sOs = aoRow[14];
self.sOsVersion = aoRow[15];
self.sCpuVendor = aoRow[16];
self.sCpuArch = aoRow[17];
self.sCpuName = aoRow[18];
self.lCpuRevision = aoRow[19];
self.cCpus = aoRow[20];
self.fCpuHwVirt = aoRow[21];
self.fCpuNestedPaging = aoRow[22];
self.fCpu64BitGuest = aoRow[23];
self.fChipsetIoMmu = aoRow[24];
self.cMbMemory = aoRow[25];
self.cMbScratch = aoRow[26];
self.sReport = aoRow[27];
self.iTestBoxScriptRev = aoRow[28];
self.iPythonHexVersion = aoRow[29];
self.enmPendingCmd = aoRow[30];
return self;
def initFromDbWithId(self, oDb, idTestBox, tsNow = None, sPeriodBack = None):
"""
Initialize the object from the database.
"""
oDb.execute(self.formatSimpleNowAndPeriodQuery(oDb,
'SELECT *\n'
'FROM TestBoxes\n'
'WHERE idTestBox = %s\n'
, ( idTestBox, ), tsNow, sPeriodBack));
aoRow = oDb.fetchOne()
if aoRow is None:
raise TMExceptionBase('idTestBox=%s not found (tsNow=%s sPeriodBack=%s)' % (idTestBox, tsNow, sPeriodBack,));
return self.initFromDbRow(aoRow);
def initFromDbWithGenId(self, oDb, idGenTestBox):
"""
Initialize the object from the database.
"""
oDb.execute('SELECT *\n'
'FROM TestBoxes\n'
'WHERE idGenTestBox = %s\n'
, (idGenTestBox, ) );
return self.initFromDbRow(oDb.fetchOne());
def _validateAndConvertWorker(self, asAllowNullAttributes, oDb):
# Override to do extra ipLom checks.
dErrors = ModelDataBase._validateAndConvertWorker(self, asAllowNullAttributes, oDb);
if self.ksParam_ipLom not in dErrors \
and self.ksParam_enmLomKind not in dErrors \
and self.enmLomKind != self.ksLomKind_None \
and self.ipLom is None:
dErrors[self.ksParam_ipLom] = 'Light-out-management IP is mandatory and a LOM is selected.'
return dErrors;
def formatPythonVersion(self):
"""
Unbuttons the version number and formats it as a version string.
"""
if self.iPythonHexVersion is None:
return 'N/A';
return 'v%d.%d.%d.%d' \
% ( self.iPythonHexVersion >> 24,
(self.iPythonHexVersion >> 16) & 0xff,
(self.iPythonHexVersion >> 8) & 0xff,
self.iPythonHexVersion & 0xff);
def getCpuFamily(self):
""" Returns the CPU family for a x86 or amd64 testboxes."""
if self.lCpuRevision is None:
return 0;
return (self.lCpuRevision >> 24 & 0xff);
def getCpuModel(self):
""" Returns the CPU model for a x86 or amd64 testboxes."""
if self.lCpuRevision is None:
return 0;
return (self.lCpuRevision >> 8 & 0xffff);
def getCpuStepping(self):
""" Returns the CPU stepping for a x86 or amd64 testboxes."""
if self.lCpuRevision is None:
return 0;
return (self.lCpuRevision & 0xff);
class TestBoxLogic(ModelLogicBase):
"""
TestBox logic.
"""
def __init__(self, oDb):
ModelLogicBase.__init__(self, oDb);
def tryFetchTestBoxByUuid(self, sTestBoxUuid):
"""
Tries to fetch a testbox by its UUID alone.
"""
self._oDb.execute('SELECT *\n'
'FROM TestBoxes\n'
'WHERE uuidSystem = %s\n'
' AND tsExpire = \'infinity\'::timestamp\n'
'ORDER BY tsEffective DESC\n',
(sTestBoxUuid,));
if self._oDb.getRowCount() == 0:
return None;
if self._oDb.getRowCount() != 1:
raise TMExceptionBase('Database integrity error: %u hits' % (self._oDb.getRowCount(),));
oData = TestBoxData();
oData.initFromDbRow(self._oDb.fetchOne());
return oData;
def fetchForListing(self, iStart, cMaxRows, tsNow):
"""
Fetches testboxes for listing.
Returns an array (list) of TestBoxData items, empty list if none. The
TestBoxData instances have an extra oStatus member that is either None or
a TestBoxStatusData instance, and a member tsCurrent holding
CURRENT_TIMESTAMP.
Raises exception on error.
"""
from testmanager.core.testboxstatus import TestBoxStatusData;
if tsNow is None:
self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
'FROM TestBoxes\n'
'LEFT OUTER JOIN TestBoxStatuses ON (\n'
' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
'WHERE tsExpire = \'infinity\'::TIMESTAMP\n'
'ORDER BY sName\n'
'LIMIT %s OFFSET %s\n'
, (cMaxRows, iStart,));
else:
self._oDb.execute('SELECT TestBoxes.*, TestBoxStatuses.*\n'
'FROM TestBoxes\n'
'LEFT OUTER JOIN TestBoxStatuses ON (\n'
' TestBoxStatuses.idTestBox = TestBoxes.idTestBox )\n'
'WHERE tsExpire > %s\n'
' AND tsEffective <= %s\n'
'ORDER BY sName\n'
'LIMIT %s OFFSET %s\n'
, (tsNow, tsNow, cMaxRows, iStart,));
aoRows = [];
for aoOne in self._oDb.fetchAll():
oTestBox = TestBoxData().initFromDbRow(aoOne);
oTestBox.tsCurrent = self._oDb.getCurrentTimestamp(); # pylint: disable=W0201
oTestBox.oStatus = None; # pylint: disable=W0201
if aoOne[31] is not None:
oTestBox.oStatus = TestBoxStatusData().initFromDbRow(aoOne[31:]); # pylint: disable=W0201
aoRows.append(oTestBox);
return aoRows;
def fetchForChangeLog(self, idTestBox, iStart, cMaxRows, tsNow): # pylint: disable=R0914
"""
Fetches change log entries for a testbox.
Returns an array of ChangeLogEntry instance and an indicator whether
there are more entries.
Raises exception on error.
"""
if tsNow is None:
tsNow = self._oDb.getCurrentTimestamp();
self._oDb.execute('SELECT TestBoxes.*, Users.sUsername\n'
'FROM TestBoxes\n'
'LEFT OUTER JOIN Users \n'
' ON ( TestBoxes.uidAuthor = Users.uid\n'
' AND Users.tsEffective <= TestBoxes.tsEffective\n'
' AND Users.tsExpire > TestBoxes.tsEffective)\n'
'WHERE TestBoxes.tsEffective <= %s\n'
' AND TestBoxes.idTestBox = %s\n'
'ORDER BY TestBoxes.tsExpire DESC\n'
'LIMIT %s OFFSET %s\n'
, (tsNow, idTestBox, cMaxRows + 1, iStart,));
aoRows = [];
for _ in range(self._oDb.getRowCount()):
oRow = self._oDb.fetchOne();
aoRows.append([TestBoxData().initFromDbRow(oRow), oRow[-1],]);
# Calculate the changes.
aoEntries = [];
for i in range(0, len(aoRows) - 1):
(oNew, sAuthor) = aoRows[i];
(oOld, _ ) = aoRows[i + 1];
aoChanges = [];
for sAttr in oNew.getDataAttributes():
if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor']:
oOldAttr = getattr(oOld, sAttr);
oNewAttr = getattr(oNew, sAttr);
if oOldAttr != oNewAttr:
aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)));
aoEntries.append(ChangeLogEntry(oNew.uidAuthor, sAuthor, oNew.tsEffective, oNew.tsExpire, oNew, oOld, aoChanges));
# If we're at the end of the log, add the initial entry.
if len(aoRows) <= cMaxRows and len(aoRows) > 0:
(oNew, sAuthor) = aoRows[-1];
aoEntries.append(ChangeLogEntry(oNew.uidAuthor, aoRows[-1][1], oNew.tsEffective, oNew.tsExpire, oNew, None, []));
return (aoEntries, len(aoRows) > cMaxRows);
def addEntry(self, oData, uidAuthor, fCommit = False):
"""
Creates a testbox in the database.
Returns the testbox ID, testbox generation ID and effective timestamp
of the created testbox on success. Throws error on failure.
"""
dDataErrors = oData.validateAndConvert(self._oDb);
if len(dDataErrors) > 0:
raise TMExceptionBase('Invalid data passed to create(): %s' % (dDataErrors,));
self._oDb.execute('INSERT INTO TestBoxes (\n'
' idTestBox,\n'
' tsEffective,\n'
' tsExpire,\n'
' uidAuthor,\n'
' idGenTestBox,\n'
' ip,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' sReport,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' enmPendingCmd\n'
' )'
'VALUES (\n'
' DEFAULT,\n'
' CURRENT_TIMESTAMP,\n'
' DEFAULT,\n'
' %s,\n' # uidAuthor
' DEFAULT,\n'
' %s,\n' # ip
' %s,\n' # uuidSystem
' %s,\n' # sName
' %s,\n' # sDescription
' %s,\n' # idSchedGroup
' %s,\n' # fEnabled
' %s,\n' # enmLomKind
' %s,\n' # ipLom
' %s,\n' # pctScaleTimeout
' %s,\n' # sOs
' %s,\n' # sOsVersion
' %s,\n' # sCpuVendor
' %s,\n' # sCpuArch
' %s,\n' # sCpuName
' %s,\n' # lCpuRevision
' %s,\n' # cCpus
' %s,\n' # fCpuHwVirt
' %s,\n' # fCpuNestedPaging
' %s,\n' # fCpu64BitGuest
' %s,\n' # fChipsetIoMmu
' %s,\n' # cMbMemory
' %s,\n' # cMbScratch
' %s,\n' # sReport
' %s,\n' # iTestBoxScriptRev
' %s,\n' # iPythonHexVersion
' %s\n' # enmPendingCmd
' )\n'
'RETURNING idTestBox, idGenTestBox, tsEffective'
, (uidAuthor,
oData.ip,
oData.uuidSystem,
oData.sName,
oData.sDescription,
oData.idSchedGroup,
oData.fEnabled,
oData.enmLomKind,
oData.ipLom,
oData.pctScaleTimeout,
oData.sOs,
oData.sOsVersion,
oData.sCpuVendor,
oData.sCpuArch,
oData.sCpuName,
oData.lCpuRevision,
oData.cCpus,
oData.fCpuHwVirt,
oData.fCpuNestedPaging,
oData.fCpu64BitGuest,
oData.fChipsetIoMmu,
oData.cMbMemory,
oData.cMbScratch,
oData.sReport,
oData.iTestBoxScriptRev,
oData.iPythonHexVersion,
oData.enmPendingCmd
)
);
oRow = self._oDb.fetchOne();
self._oDb.maybeCommit(fCommit);
return (oRow[0], oRow[1], oRow[2]);
def editEntry(self, oData, uidAuthor, fCommit = False):
"""
Data edit update, web UI is the primary user.
Returns the new generation ID and effective date.
"""
dDataErrors = oData.validateAndConvert(self._oDb);
if len(dDataErrors) > 0:
raise TMExceptionBase('Invalid data passed to create(): %s' % (dDataErrors,));
## @todo check if the data changed.
self._oDb.execute('UPDATE ONLY TestBoxes\n'
'SET tsExpire = CURRENT_TIMESTAMP\n'
'WHERE idGenTestBox = %s\n'
' AND tsExpire = \'infinity\'::timestamp\n'
'RETURNING tsExpire\n',
(oData.idGenTestBox,));
try:
tsEffective = self._oDb.fetchOne()[0];
# Would be easier to do this using an insert or update hook, I think. Much easier.
self._oDb.execute('INSERT INTO TestBoxes (\n'
' idGenTestBox,\n'
' idTestBox,\n'
' tsEffective,\n'
' uidAuthor,\n'
' ip,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' enmPendingCmd\n'
' )\n'
'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
' idTestBox,\n'
' %s,\n' # tsEffective
' %s,\n' # uidAuthor
' %s,\n' # ip
' %s,\n' # uuidSystem
' %s,\n' # sName
' %s,\n' # sDescription
' %s,\n' # idSchedGroup
' %s,\n' # fEnabled
' %s,\n' # enmLomKind
' %s,\n' # ipLom
' %s,\n' # pctScaleTimeout
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' %s\n' # enmPendingCmd
'FROM TestBoxes\n'
'WHERE idGenTestBox = %s\n'
'RETURNING idGenTestBox, tsEffective'
, (tsEffective,
uidAuthor,
oData.ip,
oData.uuidSystem,
oData.sName,
oData.sDescription,
oData.idSchedGroup,
oData.fEnabled,
oData.enmLomKind,
oData.ipLom,
oData.pctScaleTimeout,
oData.enmPendingCmd,
oData.idGenTestBox,
));
aRow = self._oDb.fetchOne();
if aRow is None:
raise TMExceptionBase('Insert failed? oRow=None');
idGenTestBox = aRow[0];
tsEffective = aRow[1];
self._oDb.maybeCommit(fCommit);
except:
self._oDb.rollback();
raise;
return (idGenTestBox, tsEffective);
def updateOnSignOn(self, idTestBox, idGenTestBox, sTestBoxAddr, sOs, sOsVersion, # pylint: disable=R0913,R0914
sCpuVendor, sCpuArch, sCpuName, lCpuRevision, cCpus, fCpuHwVirt, fCpuNestedPaging, fCpu64BitGuest,
fChipsetIoMmu, cMbMemory, cMbScratch, sReport, iTestBoxScriptRev, iPythonHexVersion):
"""
Update the testbox attributes automatically on behalf of the testbox script.
Returns the new generation id on success, raises an exception on failure.
"""
try:
# Would be easier to do this using an insert or update hook, I think. Much easier.
self._oDb.execute('UPDATE ONLY TestBoxes\n'
'SET tsExpire = CURRENT_TIMESTAMP\n'
'WHERE idGenTestBox = %s\n'
' AND tsExpire = \'infinity\'::timestamp\n'
'RETURNING tsExpire\n',
(idGenTestBox,));
tsEffective = self._oDb.fetchOne()[0];
self._oDb.execute('INSERT INTO TestBoxes (\n'
' idGenTestBox,\n'
' idTestBox,\n'
' tsEffective,\n'
' uidAuthor,\n'
' ip,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' sReport,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' enmPendingCmd\n'
' )\n'
'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
' %s,\n'
' %s,\n'
' NULL,\n' # uidAuthor
' %s,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' %s,\n' # sOs
' %s,\n' # sOsVersion
' %s,\n' # sCpuVendor
' %s,\n' # sCpuArch
' %s,\n' # sCpuName
' %s,\n' # lCpuRevision
' %s,\n' # cCpus
' %s,\n' # fCpuHwVirt
' %s,\n' # fCpuNestedPaging
' %s,\n' # fCpu64BitGuest
' %s,\n' # fChipsetIoMmu
' %s,\n' # cMbMemory
' %s,\n' # cMbScratch
' %s,\n' # sReport
' %s,\n' # iTestBoxScriptRev
' %s,\n' # iPythonHexVersion
' enmPendingCmd\n'
'FROM TestBoxes\n'
'WHERE idGenTestBox = %s\n'
'RETURNING idGenTestBox'
, (idTestBox,
tsEffective,
sTestBoxAddr,
sOs,
sOsVersion,
sCpuVendor,
sCpuArch,
sCpuName,
lCpuRevision,
cCpus,
fCpuHwVirt,
fCpuNestedPaging,
fCpu64BitGuest,
fChipsetIoMmu,
cMbMemory,
cMbScratch,
sReport,
iTestBoxScriptRev,
iPythonHexVersion,
idGenTestBox,
));
idGenTestBox = self._oDb.fetchOne()[0];
self._oDb.commit();
except:
self._oDb.rollback();
raise;
return idGenTestBox;
def setCommand(self, idTestBox, sOldCommand, sNewCommand, uidAuthor = None, fCommit = False):
"""
Sets or resets the pending command on a testbox.
Returns (idGenTestBox, tsEffective) of the new row.
"""
try:
# Would be easier to do this using an insert or update hook, I think. Much easier.
self._oDb.execute('UPDATE ONLY TestBoxes\n'
'SET tsExpire = CURRENT_TIMESTAMP\n'
'WHERE idTestBox = %s\n'
' AND tsExpire = \'infinity\'::timestamp\n'
' AND enmPendingCmd = %s\n'
'RETURNING tsExpire\n',
(idTestBox, sOldCommand,));
tsEffective = self._oDb.fetchOne()[0];
self._oDb.execute('INSERT INTO TestBoxes (\n'
' idGenTestBox,\n'
' idTestBox,\n'
' tsEffective,\n'
' uidAuthor,\n'
' ip,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' sReport,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' enmPendingCmd\n'
' )\n'
'SELECT NEXTVAL(\'TestBoxGenIdSeq\'),\n'
' %s,\n' # idTestBox
' %s,\n' # tsEffective
' %s,\n' # uidAuthor
' ip,\n'
' uuidSystem,\n'
' sName,\n'
' sDescription,\n'
' idSchedGroup,\n'
' fEnabled,\n'
' enmLomKind,\n'
' ipLom,\n'
' pctScaleTimeout,\n'
' sOs,\n'
' sOsVersion,\n'
' sCpuVendor,\n'
' sCpuArch,\n'
' sCpuName,\n'
' lCpuRevision,\n'
' cCpus,\n'
' fCpuHwVirt,\n'
' fCpuNestedPaging,\n'
' fCpu64BitGuest,\n'
' fChipsetIoMmu,\n'
' cMbMemory,\n'
' cMbScratch,\n'
' sReport,\n'
' iTestBoxScriptRev,\n'
' iPythonHexVersion,\n'
' %s\n' # enmPendingCmd
'FROM TestBoxes\n'
'WHERE idTestBox = %s\n'
' AND tsExpire = %s\n'
' AND enmPendingCmd = %s\n'
'RETURNING idGenTestBox'
, (idTestBox,
tsEffective,
uidAuthor,
sNewCommand,
idTestBox,
tsEffective,
sOldCommand,
));
idGenTestBox = self._oDb.fetchOne()[0];
if fCommit is True:
self._oDb.commit();
except:
self._oDb.rollback();
raise;
return (idGenTestBox, tsEffective);
def getAll(self):
"""
Retrieve list of all registered Test Box records from DB.
"""
self._oDb.execute('SELECT *\n'
'FROM TestBoxes\n'
'WHERE tsExpire=\'infinity\'::timestamp;')
aaoRows = self._oDb.fetchAll()
aoRet = []
for aoRow in aaoRows:
aoRet.append(TestBoxData().initFromDbRow(aoRow))
return aoRet
def _historize(self, idTestBox, uidAuthor, tsExpire = None):
""" Historizes the current entry. """
if tsExpire is None:
self._oDb.execute('UPDATE TestBoxes\n'
'SET tsExpire = CURRENT_TIMESTAMP,\n'
' uidAuthor = %s\n'
'WHERE idTestBox = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, (uidAuthor, idTestBox,));
else:
self._oDb.execute('UPDATE TestBoxes\n'
'SET tsExpire = %s,\n'
' uidAuthor = %s\n'
'WHERE idTestBox = %s\n'
' AND tsExpire = \'infinity\'::TIMESTAMP\n'
, (uidAuthor, tsExpire, idTestBox,));
return True;
def removeEntry(self, uidAuthor, idTestBox, fCascade = False, fCommit=False):
"""
Delete user account
"""
_ = fCascade;
fRc = self._historize(idTestBox, uidAuthor, None);
if fRc:
self._oDb.maybeCommit(fCommit);
return fRc
#
# Unit testing.
#
# pylint: disable=C0111
class TestBoxDataTestCase(ModelDataBaseTestCase):
def setUp(self):
self.aoSamples = [TestBoxData(),];
if __name__ == '__main__':
unittest.main();
# not reached.