Predicates
A predicate is a simple truth checker object inheriting from Predicate.
The following :voD:`VoD <http>` gives a quick introduction to predicate-based testing.
When a Predicate is tested for truth, a user-defined checking behavior is
automatically invoked. Thus, complex testing may be wrapped up into a simple
truth test.
Let’s illustrate the concept of a Predicate by creating a new predicate class
that accepts a number when it is constructed. The predicate only tests
True when that number is even:
from genie.predcore import Predicate
class IsEvenPredicate(Predicate):
''' Predicate that tests True if the input value is even '''
def __init__(self, value):
self._value = value
def dump(self):
return "IsEvenPredicate({})".format(self._value)
def test(self):
return (self._value % 2 == 0)
numsToList = [1, 6, 8, 9]
for num in numsToList:
pred = IsEvenPredicate(num)
print (" Testing {}".format(pred.dump()))
if pred :
print ("{} is even.".format(num))
IsEvenPredicate(8).assert_test()
IsEvenPredicate(9).assert_test()
IsEvenPredicate(8)()
IsEvenPredicate(9)()
This example produces the following output (notice the
assert_test silently passes when the number being
tested is even but raises a signal when the number is odd):
Testing IsEvenPredicate(1)
Testing IsEvenPredicate(6)
6 is even.
Testing IsEvenPredicate(8)
8 is even.
Testing IsEvenPredicate(9)
(signal gets raised)
genie.predcore.PredicateTestedFalseSignal: IsEvenPredicate(9)
True
False
A Predicate is a class that has the following properties:
It has a
testmethod that executes a test and returnsTrueorFalse. This method is overloaded and typically runs CLI commands on a device, determining the truth value to return based on the device’s response.Whenever an object inheriting from
Predicateis tested for truth (for example, in anifstatement), itstestmethod is automatically called.It has an
assert_testmethod that is used to ensure a predicate testsTrue(if it doesn’t, a signal is raised).It has an optional
dumpmethod that, if overloaded, provides a powerful way to pass relevant information when the predicate does not test to the expected value.It can be called in order to perform its truth test, returning either
TrueorFalse. This makesPredicateinteroperable with commands such asthreading.Condition.wait_fororasyncio.wait_for.
Let’s see a more device-centric example. Assume we have an
InterfaceUpPredicate class that has its test method
overloaded to run a show interface command on a device, parse the result,
and determine if the requested interface is operationally and administratively
up:
if_name = 'GigabitEthernet0/0'
check_pred = InterfaceUpPredicate(
device = my_device,
interface_name = if_name)
if check_pred:
print("Interface is up.")
else:
print("Interface is down.")
Predicates that contain other Predicates
Let’s say that we want to validate that a series of interfaces are up by doing only a single truth test.
This can be done using AndPredicate, which is a predicate that accepts a
series of user-defined objects that are able to be tested for truth. These
objects don’t have to be Predicates, but in this example they
will be.
Every time this predicate is tested for truth, all contained objects are
scheduled for re-testing. If any object in the series tests False, then the
predicate itself tests False and the remaining objects remain un-tested.
Let’s assume that InterfaceUpPredicate can accept an interface object
that knows its attached device and interface name:
all_interfaces_up_pred = AndPredicate(
InterfaceUpPredicate (interface_object = interface_1),
InterfaceUpPredicate (interface_object = interface_2),
InterfaceUpPredicate (interface_object = interface_3),
)
if all_interfaces_up_pred:
print ("All interfaces are up.")
else:
print ("Not all interfaces are up.")
Timed Looping Predicates
Predicates that periodically test a series of objects for truth are particularly
useful when doing initial test setup. predcore provides several such
pre-requisite classes.
Prerequisite
When a Prerequisite object is tested for truth, it periodically checks a
series of user-defined contained objects for truth until either they all test
True or a timeout is hit. These objects are typically
Predicates but could be any object able to be tested for truth.
Every time a Prerequisite is tested for truth, all contained objects are
re-tested.
The following snippet illustrates how a Prerequisite can be created and
checked:
check_pred = InterfaceUpPredicate(
device = my_device,
interface_name = if_name)
interfaceIsUpPred = Prerequisite(check_pred, timeout=30)
if interfaceIsUpPred:
print("Interface came up within 30 seconds.")
else:
print("Interface did not come up within 30 seconds.")
The assert_test method is most commonly used to check
a pre-requisite. If the pre-requisite fails then an exception is raised that
carries with it debug information describing the failure reason:
try:
interfaceIsUpPred.assert_test()
except PredicateTestedFalseSignal as e:
log.error(e)
print("Interface did not come up within 30 seconds."
PrerequisiteWhile
This is a looping predicate that ensures a series of user-defined objects all
continue to test True for a minimum length of time.
The following snippet shows how the PrerequisiteWhile class may be used
to ensure an interface stays up for a minimum length of time. Notice the
assert_test method also accepts a user-defined
failure string that is contained by the exception produced should the assertion
fail.
check_pred = InterfaceUpPredicate(
device = my_device,
interface_name = if_name)
interfaceStaysUpPred = PrerequisiteWhile(check_pred, timeout=30)
try:
interfaceStaysUpPred.assert_test(
"Interface did not stay up for 30 seconds.")
except PredicateTestedFalseSignal as e:
log.error(e)
Finally, here’s an example of a test that ensures a series of interfaces stay up for a minimum length of time. If the test fails, the interface that did not stay up is included in the signal and is logged to facilitate debugging:
interfaceStaysUpPred = PrerequisiteWhile(
InterfaceUpPredicate (interface_object = interface_1),
InterfaceUpPredicate (interface_object = interface_2),
InterfaceUpPredicate (interface_object = interface_3),
timeout=30)
try:
interfaceStaysUpPred.assert_test(
"Interfaces did not stay up for 30 seconds.")
except PredicateTestedFalseSignal as e:
log.error(e)