# -*- coding: utf-8 -*-
# $Id$
# pylint: disable=C0302
"""
Test Manager Core - Base Class(es).
"""
__copyright__ = \
"""
Copyright (C) 2012-2014 Oracle Corporation
This file is part of VirtualBox Open Source Edition (OSE), as
available from http://www.virtualbox.org. This file is free software;
General Public License (GPL) as published by the Free Software
Foundation, in version 2 as it comes in the "COPYING" file of the
VirtualBox OSE distribution. VirtualBox OSE is distributed in the
hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
The contents of this file may alternatively be used under the terms
of the Common Development and Distribution License Version 1.0
(CDDL) only, as it comes in the "COPYING.CDDL" file of the
VirtualBox OSE distribution, in which case the provisions of the
CDDL are applicable instead of those of the GPL.
You may elect to license modified versions of this file under the
terms and conditions of either the GPL or the CDDL or both.
"""
# Standard python imports.
import copy;
import re;
import socket;
import sys;
import uuid;
import unittest;
# Validation Kit imports.
# Python 3 hacks:
"""
For exceptions raised by any TestManager component.
"""
pass;
"""
Too many rows in the result.
Used by ModelLogicBase decendants.
"""
pass;
"""
Something all classes in the logical model inherits from.
Not sure if 'logical model' is the right term here.
Will see if it has any purpose later on...
"""
pass;
"""
Something all classes in the data classes in the logical model inherits from.
"""
## Child classes can use this to list array attributes which should use
# an empty array ([]) instead of None as database NULL value.
kasAltArrayNull = [];
#
# Standard methods implemented by combining python magic and hungarian prefixes.
#
"""
Returns a list of data attributes.
"""
asRet = [];
continue;
continue;
return asRet;
"""
Initialize this object with the values from another instance (child
class instance is accepted).
This serves as a kind of copy constructor.
Returns self. May raise exception if the type of other object differs
or is damaged.
"""
return self;
"""
Returns the hungarian prefix of the given name.
"""
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']:
return sName[:i];
return sName;
"""
Returns a list of parameter NULL values, with the preferred one being
the first element.
Child classes can override this to handle one or more attributes specially.
"""
return [-1, '', '-1',];
elif sPrefix == 'f':
return ['',];
return ['',];
return [[], '', None]; ## @todo ??
elif sPrefix == 'bm':
return ['', [],]; ## @todo bitmaps.
"""
Checks if the specified attribute value indicates NULL.
Note! This isn't entirely kosher actually.
"""
if oValue is None:
return True;
return oValue in aoNilValues;
"""
Converts an attribute from parameter NULL to database NULL value.
Returns the new attribute value.
"""
if oValue in aoNullValues:
#
# Perform deep conversion on ModelDataBase object and lists of them.
#
oValue[i].convertFromParamNull();
return oValue;
"""
Converts from parameter NULL values to database NULL values (None).
Returns self.
"""
return self;
"""
Converts an attribute from database NULL to a sepcial value we can pass
thru parameter list.
Returns the new attribute value.
"""
if oValue is None:
#
# Perform deep conversion on ModelDataBase object and lists of them.
#
oValue[i].convertToParamNull();
return oValue;
"""
Converts from database NULL values (None) to special values we can
pass thru parameters list.
Returns self.
"""
return self;
"""
Validates and convert one attribute.
Returns the converted value.
Child classes can override this to handle one or more attributes specially.
Note! oDb can be None.
"""
(oNewValue, sError) = self.validateInt( oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
elif sPrefix == 'f':
if oValue is '' and not fAllowNull: oValue = '0'; # HACK ALERT! Checkboxes are only added when checked.
(oNewValue, sError) = self.validateBool(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
elif sPrefix == 'ts':
elif sPrefix == 'ip':
elif sPrefix == 'uuid':
(oNewValue, sError) = self.validateUuid(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
elif sPrefix == 'enm':
elif sPrefix == 's':
## @todo al.
elif sPrefix == 'aid':
(oNewValue, sError) = self.validateListOfInts(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
elif sPrefix == 'as':
(oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull,
elif sPrefix == 'bm':
## @todo figure out bitfields.
(oNewValue, sError) = self.validateListOfStr(oValue, aoNilValues = aoNilValues, fAllowNull = fAllowNull);
else:
"""
Worker for implementing validateAndConvert().
"""
aoNilValues.append(None);
if sError is not None:
return dErrors;
"""
Validates the input and converts valid fields to their right type.
Returns a dictionary with per field reports, only invalid fields will
be returned, so an empty dictionary means that the data is valid.
The dictionary keys are ksParam_*.
Child classes can override _validateAndConvertAttribute to handle
selected fields specially. There are also a few class variables that
can be used to advice the validation: kcchMin_sAttr, kcchMax_sAttr,
kiMin_iAttr, kiMax_iAttr, klMin_lAttr, klMax_lAttr,
kasValidValues_enmAttr, and kasAllowNullAttributes.
"""
"""
Calculate the attribute value when initialized from a parameter.
Returns the new value, with parameter NULL values. Raises exception on
invalid parameter value.
Child classes can override to do special parameter conversion jobs.
"""
if fStrict:
if sPrefix == 'f':
# HACK ALERT! Checkboxes are only present when checked, so we always have to provide a default.
# HACK ALERT! List are not present if empty.
else:
else:
else:
return oNewValue;
"""
Initialize the object from parameters.
The input is not validated at all, except that all parameters must be
present when fStrict is True.
Returns self. Raises exception on invalid parameter value.
Note! The returned object has parameter NULL values, not database ones!
"""
oNewValue = self.convertParamToAttribute(sAttr, getattr(self, 'ksParam_' + sAttr), oValue, oDisp, fStrict);
return self;
"""
Called to compare two attribute values and python thinks differs.
Child classes can override this to do special compares of things like arrays.
"""
# Just in case someone uses it directly.
return True;
#
# Timestamps can be both string (param) and object (db)
# depending on the data source. Compare string values to make
# sure we're doing the right thing here.
#
if sPrefix == 'ts':
#
# Some generic code handling ModelDataBase children.
#
return False;
return False;
return True;
_ = sAttr;
return False;
""" Compares two instances. """
# Delegate the final decision to an overridable method.
return False;
return True;
""" Compares two instances, omitting the given attributes. """
if sAttr not in asExcludeAttrs \
# Delegate the final decision to an overridable method.
return False;
return True;
"""
Reinitializes the object to (database) NULL values.
Returns self.
"""
return self;
"""
Stringifies the object.
Returns string representation.
"""
sMembers = '';
if sMembers == '':
#
# New validation helpers.
#
# These all return (oValue, sError), where sError is None when the value
# is valid and an error message when not. On success and in case of
# range errors, oValue is converted into the requested type.
#
def validateInt(sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, '']), fAllowNull = True):
""" Validates an integer field. """
if sValue in aoNilValues:
if fAllowNull:
return (sValue, 'Mandatory.');
try:
else:
except:
return (sValue, 'Not an integer');
if iValue in aoNilValues:
return (iValue, None);
def validateLong(sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, '']), fAllowNull = True):
""" Validates an long integer field. """
if sValue in aoNilValues:
if fAllowNull:
return (sValue, 'Mandatory.');
try:
else:
except:
return (sValue, 'Not a long integer');
if lValue in aoNilValues:
return (lValue, None);
""" Validates a timestamp field. """
if sValue in aoNilValues:
return (sValue, None);
sError = None;
oRes = re.match(r'(\d{4})-([01]\d)-([0123])\d ([012]\d):[0-5]\d:([0-6]\d).\d{6}[+-](\d\d):(\d\d)', sValue);
if oRes is not None \
sError = 'Invalid timezone offset.';
else:
return (sValue, 'Invalid timestamp length.');
if oRes is None:
sError = 'Invalid timestamp (format: 2012-10-08 01:54:06.364207+02:00).';
else:
else:
if iMonth > 12:
sError = 'Invalid timestamp month.';
sError = 'Invalid timestamp day-of-month (%02d has %d days).' % (iMonth, acDaysOfMonth[iMonth - 1]);
elif iHour > 23:
sError = 'Invalid timestamp hour.'
elif iSec >= 61:
sError = 'Invalid timestamp second.'
elif iSec >= 60:
sError = 'Invalid timestamp: no leap seconds, please.'
""" Validates an IP address field. """
if sValue in aoNilValues:
if sValue == '::1':
return (sValue, None);
try:
except:
try:
except:
return (sValue, 'Not a valid IP address.');
return (sValue, None);
""" Validates a boolean field. """
if sValue in aoNilValues:
return (True, None);
return (False, None);
return (sValue, 'Invalid boolean value.');
""" Validates an UUID field. """
if sValue in aoNilValues:
try:
except:
return (sValue, 'Invalid UUID value.');
return (sValue, None);
def validateWord(sValue, cchMin = 1, cchMax = 64, asValid = None, aoNilValues = tuple([None, '']), fAllowNull = True):
""" Validates a word field. """
if sValue in aoNilValues:
sError = 'Single word ([a-zA-Z0-9_-]), please.';
else:
sError = None;
def validateStr(sValue, cchMin = 0, cchMax = 4096, aoNilValues = tuple([None, '']), fAllowNull = True, fAllowUnicodeSymbols = False): # pylint: disable=C0301
""" Validates a string field. """
if sValue in aoNilValues:
sError = 'Non-ascii characters not allowed'
else:
sError = None;
""" Validates a email field."""
if sValue in aoNilValues:
return (sValue,'Invalid e-mail format.');
return (sValue, None);
""" Validate a list of some uniform values. Returns a copy of the list (if list it is). """
return (asValues, None);
""" Validates a list of text items."""
return (asValues, 'Invalid item data type.');
if not fAllowNull and cchMin is None:
cchMin = 1;
else:
continue;
if sError is None:
else:
def validateListOfInts(asValues, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([[], None]), fAllowNull = True):
""" Validates a list of integer items."""
sThisErr = '';
try:
except:
else:
else:
continue;
if sError is None:
else:
#
# Old validation helpers.
#
def _validateInt(dErrors, sName, sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, ''])):
""" Validates an integer field. """
if sError is not None:
return sValue;
def _validateIntNN(dErrors, sName, sValue, iMin = 0, iMax = 0x7ffffffe, aoNilValues = tuple([-1, None, ''])):
""" Validates an integer field, not null. """
if sError is not None:
return sValue;
def _validateLong(dErrors, sName, sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, ''])):
""" Validates an long integer field. """
if sError is not None:
return sValue;
def _validateLongNN(dErrors, sName, sValue, lMin = 0, lMax = None, aoNilValues = tuple([long(-1), None, ''])):
""" Validates an long integer field, not null. """
if sError is not None:
return sValue;
""" Validates a timestamp field. """
if sError is not None:
return sValue;
""" Validates a timestamp field, not null. """
if sError is not None:
return sValue;
""" Validates an IP address field. """
if sError is not None:
return sValue;
""" Validates an IP address field, not null. """
if sError is not None:
return sValue;
""" Validates a boolean field. """
if sError is not None:
return sValue;
""" Validates a boolean field, not null. """
if sError is not None:
return sValue;
""" Validates an UUID field. """
if sError is not None:
return sValue;
""" Validates an UUID field, not null. """
if sError is not None:
return sValue;
""" Validates a word field. """
if sError is not None:
return sValue;
""" Validates a boolean field, not null. """
if sError is not None:
return sValue;
""" Validates a string field. """
if sError is not None:
return sValue;
""" Validates a string field, not null. """
if sError is not None:
return sValue;
""" Validates a email field."""
if sError is not None:
return sValue;
""" Validates a email field."""
if sError is not None:
return sValue;
""" Validates a list of text items."""
(sValue, sError) = ModelDataBase.validateListOfStr(asValues, asValidValues = asValidValues, fAllowNull = True);
if sError is not None:
return sValue;
""" Validates a list of text items, not null and len >= 1."""
(sValue, sError) = ModelDataBase.validateListOfStr(asValues, asValidValues = asValidValues, fAllowNull = False);
if sError is not None:
return sValue;
#
# Various helpers.
#
"""
Formats a set of tsNow and sPeriodBack arguments for a standard testmanager
table.
If sPeriodBack is given, the query is effective for the period
(tsNow - sPeriodBack) thru (tsNow).
If tsNow isn't given, it defaults to current time.
Returns the final portion of a WHERE query (start with AND) and maybe an
ORDER BY and LIMIT bit if sPeriodBack is given.
"""
if tsNow is not None:
if sPeriodBack is not None:
' AND tsEffective <= %s\n'
'LIMIT 1\n'
else:
else:
if sPeriodBack is not None:
sRet = oDb.formatBindArgs(' AND ' + sTablePrefix + sExpCol + ' > (CURRENT_TIMESTAMP - %s::interval)\n'
'LIMIT 1\n'
, ( sPeriodBack, ));
else:
return sRet;
"""
Formats a simple query for a standard testmanager table with optional
tsNow and sPeriodBack arguments.
The sQuery and sBindArgs are passed along to oDb.formatBindArgs to form
the first part of the query. Must end with an open WHERE statement as
we'll be adding the time part starting with 'AND something...'.
See formatSimpleNowAndPeriod for tsNow and sPeriodBack description.
Returns the final portion of a WHERE query (start with AND) and maybe an
ORDER BY and LIMIT bit if sPeriodBack is given.
"""
#
# Sub-classes.
#
"""Proxy object."""
"""See WuiDispatcherBase.getStringParam."""
"""See WuiDispatcherBase.getListOfStrParams."""
"""See WuiDispatcherBase.getListOfIntParams."""
# pylint: disable=E1101,C0111,R0903
"""
Base testcase for ModelDataBase decendants.
Derive from this and override setUp.
"""
"""
Override this! Don't call super!
The subclasses are expected to set aoSamples to an array of instance
samples. The first entry must be a default object, the subsequent ones
are optional and their contents freely choosen.
"""
return;
_ = sName; _ = asValidValues;
return sDefault;
_ = sName;
return asDefaults;
return aiDefaults;
# pylint: enable=E1101,C0111,R0903
"""
Something all classes in the logic classes the logical model inherits from.
"""
#
# Note! Do not create a connection here if None, we need to DB share
# connection with all other logic objects so we can perform half
# complex transactions involving several logic objects.
#
"""
Gets the database connection.
This should only be used for instantiating other ModelLogicBase children.
"""
"""
Data class representing the changes made to one attribute.
"""
"""
A change log entry returned by the fetchChangeLog method typically
implemented by ModelLogicBase child classes.
"""