Behavior & Flow¶
This section covers native runtime behaviors of Easypy using pyats run job
:
the behavior and/or objects available while Easypy is running Jobfiles.
Easypy Return Codes¶
pyats run job
exits with Linux exit code 0
(Success) if all the
following conditions are met:
Condition |
Description |
---|---|
Some tests were ran |
At least one test script/testcase was run and reported |
Test results are available |
No exception seen while trying to access test results. |
success rate is 100% |
Total # of tests with |
All pre-job plugins ran ok |
No exception seen in any pre-job plugin. |
All pre-task plugins ran ok |
No exception seen in any pre-task plugin for all scripts in the jobfile. |
All post-task plugins ran ok |
No exception seen in any post-task plugin for all scripts in the jobfile. |
All post-job plugins ran ok |
No exception seen in any post-job plugin. |
Otherwise, the following codes will be used:
Code |
Description |
---|---|
|
Non-100% testcase success rate |
|
Incorrect command-line arguments |
|
Some errors/exceptions are seen while running plugins |
|
All other exceptions unhandled by the infrastructure (unknown) |
Return codes give test runners such as Jenkins one way of determining if the
pyats run job
run was successful.
Note
When a testscript fails to run, the success rate of the job is multiplied by the percentage of testscripts that successfully executed. This prevents the case where the success rate is 100% despite a testscript failing to run.
Graceful Termination¶
Easypy has the built-in capability to gracefully handle the exit signal SIGINT
(Ctrl-C
) from the user and its runtime Linux environment. Keep in mind that
Linux signals always propagate to the entire process group - and all processes
spawned by Easypy belong to the same process group.
When the first
SIGINT
is received, all child processes should begin aborting the current execution (quick-exit).The main Easypy process continues to wait for all child processes and Tasks to wrap-up execution. The email report, archive, upload logs etc still occur after everything finalizes.
If a second
SIGINT
is received, the main Easypy process is quickly aborted & crashed out. No subsequent handling of exceptions & erroneous states are processed. No email report, archive etc are performed.
runtime¶
When a job is running, the runtime object allows users to query to current
Easypy states, objects and input argument information. To read and refer to
runtime
attributes, define your job file’s main function with the special
runtime argument:
# Example
# -------
#
# querying runtime information inside jobfile
from pyats import easypy
# use special argument runtime in main() argument list
# the engine automatically passes in the runtime object
def main(runtime)
# pass in runtime directory as an argument to testscript
run('script.py', directory = runtime.directory)
The following attributes are currently accessible through runtime
:
runtime.env
attribute dictionary of runtime environment information.
# Runtime Environment Attributes # ------------------------------ env.argv.all command line arguments easypy was called with env.argv.custom all user arguments not recognized by easypy env.prefix pyATS virtual environment root path env.python.tag python tag, eg, cpython-34 env.python.name python name, eg, cpython env.python.version python version, eg, 3.4.1 env.python.architecture python build architecture, eg, 32bit/64bit env.env.user user id (whoami) env.host.name exec server hostname env.host.distro exec server's Linux distribution env.host.kernel exec server's Linux kernel version string env.host.architecture exec server's system architecture
runtime.directory
Location of runinfo directory.
runtime.archive
Location of the archive zip file. Note that this is a “target” location. The actual zip file is not created until the jobfile finishes execution. Set to
None
when--no-archive
option is used.runtime.testbed
current topology Testbed Object, loaded through
--testbed-file
argument. If no testbed file was provided, the testbed object is set toNone
.runtime.synchro
a built-in default multiprocessing Manager instance. This is used to synchronize data between various Easypy subprocesses, and allows users to leverage and create datastructures that can be shared between their job and script files.
runtime.mail_report
proposed jobfile report
TextEmailReport
object instance. Refer to Email Notification documentation for details.runtime.job.name
name of the current running jobfile.
runtime.job.file
full path/name of the current running jobfile.
runtime.job.uid
this job’s unique string identifier, format:
<name>.<'%Y%b%d_%H:%M:%S'
.runtime.job.image
,runtime.job.release
image name and release string information associated with this job run. By default, these values are provided by
--image
and--release
command line arguments.
Warning
runtime
shall be used solely as a read-only source of Easypy
state information. Unless otherwise advised in this documentation, any and
all write-access & monkey-patching of runtime
objects and attributes is
strictly forbidden. Doing so will void your warranty and support
contracts.
testbed¶
When a Task executes a testscript, a testbed
parameter is
always provided by default.
Testing is always done on a testbed.
The provided testbed
parameter value is the corresponding
Testbed Object instance, loaded from --testbed-file
.
If a testbed file is not provided, the value is None
, and is nevertheless
still provided to the testscript for consistency, indicating that
“no testbed was provided”.
This behavior can be overridden if testbed
argument was explicitly provided
to run()
method or Task()
class constructor.
# Example
# -------
#
# a jobfile that runs a script on 3 separate testbeds in parallel
# (cannot be done with --testbed-file argument)
from pyats.easypy import run
# import topology module
from pyats import topology
# manually load your testbed files
testbed_1 = topology.loader.load('/path/to/testbed_1.yaml')
testbed_2 = topology.loader.load('/path/to/testbed_2.yaml')
testbed_3 = topology.loader.load('/path/to/testbed_3.yaml')
def main(runtime):
# create the tasks and manually provide a testbed to run on.
tasks_1 = Task(testscript = '/path/to/my/testscript.py',
runtime = runtime,
testbed = testbed_1)
tasks_2 = Task(testscript = '/path/to/my/testscript.py',
runtime = runtime,
testbed = testbed_2)
tasks_3 = Task(testscript = '/path/to/my/testscript.py',
runtime = runtime,
testbed = testbed_3)
# now start all tasks in parallel
task_1.start()
task_2.start()
task_3.start()
# wait for all tasks to finish
task_1.wait()
task_2.wait()
task_3.wait()
Warning
the example above is intended to demonstrate the ability to provide custom
and/or override the testbed
parameter inside a job file.
Directories¶
By default, Easypy always creates a users/
directory under the current
virtual environment. Each user of this pyATS instance gets their own <user>
folder, where their runinfo and archive is located.
# Easypy Users Directory Strcture
# -------------------------------
<pyats_root>
|
|-- users
. |-- <userid>
. |-- runinfo -> runtime/runinfo folder
|-- archive -> past run log archives
|-- jobs -> user specific job file storage
`-- etc -> anything else
users/
folder is always created with 0o777
permission in order to allow
sharing of a single pyATS instance by multiple users. Each user’s own directory
is created with 0o755
to avoid other users from accidentally writing to it.
runinfo¶
During Easypy execution, runinfo
folder contains all the logs, files &
etc generated by the running jobfile and tasks. Each job is assigned its own
unique runinfo
directory, based on its name and the time of launch.
# Typical runinfo Structure
# -------------------------
#
# assuming the current job name is "example_job" and is running
runinfo
|-- example_job -> symlink to job runinfo
|-- example_job.2015Sept14_10:05:13 -> job runinfo directory
. |-- JobLog.example_job -> easypy jobfile log
|-- TaskLog.Task-1 -> Task-1 Tasklog & forked
|-- TaskLog.Task-1:pid-31526 child process logs
|-- TaskLog.Task-1:pid-31535
|-- TaskLog.Task-1:pid-31536
|-- taskresults -> folder holding current task
| |-- Task-1.common_setup.Passed results as blank files, used
| |-- Task-1.testcase_1.Passed for quickly identifing current
| `-- Task-1.testcase_2.Failed testscript progress/results.
|-- cleanresults -> folder holding current clean
| |-- Task-1.device_1.Passed results as blank files, used
| |-- Task-1.device_2.Failed for quickly identifing current
| `-- Task-1.device_3.Passed clean progress/results.
|-- reporter.log -> Reporter server log
|-- env.txt -> environment debug information
`-- example_job.py -> copy of the running jobfile
As the unique runinfo
directory of each job run is quite tedious to type,
a symlink is provided using only the job name. This allows quick reference and
access of runinfo
directory for debugging purposes. This symlink only exists
during runtime, and is removed after. Note that it should not be used as
a reliable method for automation purposes: in case two jobs of the same name
are running simultaneously, only one of them gets the symlink, depending on
whichever one ran last.
If a filename to be written to taskresults is too long, it is truncated and rather than being blank, the resulting file is made to contain the original filename and test result for tracking purposes.
archive¶
By default, at the end of Easypy execution, the contents of runinfo
is
archived into a zip file, and the jobfile runinfo
directory is deleted. This
behavior can be averted by using --no-archive
option.
Archives are stored under each user’s ./users/<userid>/archive/YY-MM/
directory, where YY-MM
represents the current year and month in double
digits, providing some level of division/classification between jobs.
Files¶
The following is a list of typical files generated by Easypy job runs, and their corresponding descriptions:
- <job-name>.py
copy of the jobfile that ran.
- <job-name>.report
copy of the email notification sent to the submitter
- TaskLog.<task-id>
TaskLog: one per jobfile task, where all messages generated in a task is stored.
- JobLog.<job-name>
overall
pyats.easypy
module log- testbed.static.yaml
Contents of the
--testbed-file
, if specified by the user.- testbed.clean.yaml
Contents of the
--clean_file
, if specified by the user.- env.txt
A dump of environment variables and cli args of this Easypy run
- reporter.log
Reporter server log file, contains a trace of XML-RPC call sequences.
- results.json
JSON result summary file generated by Reporter.
- xunit.xml,
files containing xUnit-style result reports and information required by Jenkins. These files are only generated if
--xunit
argument is provided to Easypy.- ResultsSummary.xml
XML result summary file generated by Reporter
- ResultsDetails.xml
XML result details file generated by Reporter
- CleanResultsDetails.yaml
YAML clean result details file generated by Kleenex
- Kleenex.<device-name>.log
Job-scope clean details for this device.
- Kleenex_<task-id>.<device-name>.log
Task-scope clean details for this device.