Cleaning Model

The Kleenex clean model is fairly straight forward: a common, built-in internal clean engine takes care of the common mundane infrastructure work, and the individual clean classes selected by the clean file carry out the actual work.

Under this model, these clean classes are the actual work-horses. A clean class is a device and/or platform family specific clean implementation, carrying out whatever work is necessary to recover/initialize a device with new images and set its configuration back to default (bare minimum).

The Kleenex module does not come with a built-in, universal clean implementation. Rather, it features BaseCleaner: a base class template, offering a set of standards, guidelines, rules & tools for anyone to build on.

Clean Engine

  • is an integral, non-changeable internal component to Kleenex

  • sets up runtime directory & logfiles

  • loading and parsing Clean File and testbed file (if necessary)

  • applying clean file content towards testbed device objects

  • error handling, email notification, etc.

  • running the corresponding clean on all testbed devices simultaenously in parallel

    • If -pdb is specified via the command line,

      • devices are cleaned serially

      • if a cleaner throws an exception an interactive debugger is launched.

Pictorial View of Clean Engine
------------------------------

+--------------+
|  clean file  |---.             +-------------+
+--------------+    \   input    |             |
                     >---------> | CleanEngine |
+--------------+    /            |             |
| testbed file |---'             +-------------+
+--------------+                        |
                                        |
                                        |  fork   +--------------------+
                                        |---------| cleaner: Cleaner-1 |
                                        |         | target : Device-1  |
                                        |         +--------------------+
                                        |
                                        |  fork   +--------------------+
                                        |---------| cleaner: Cleaner-2 |
                                        |         | target : Device-2  |
                                        |         +--------------------+
                                        |
                                        |  fork   +--------------------+
                                        |---------| cleaner: Cleaner-3 |
                                        |         | target : Device-3  |
                                        |         +--------------------+
                                        |
                                       etc...

Clean Classes

  • clean classes are only responsible of handling/cleaning one device at a time, performing what is necessary to do the act of “clean”

  • clean classes must inherit from the BaseCleaner class.

  • clean classes must implement at minimum, the clean() method. This is the primary entry point to start cleaning a device. This method shall accept a Device object as argument, which instructs it which device to clean.

  • clean classes should only pull their required clean-related information directly from the provided Device object’s clean attribute dictionary.

  • cleaners are allowed to modify device.clean with discovered device metadata, which then appears in the user’s Testbed Object passed via the testbed parameter.

When clean classes are referenced in the clean file (using cleaners: block), they are instantiated using the key/values given under its definition block. The engine then calls the clean() method of that class as entry-point to start cleaning the corresponding device.

# Example
# -------
#
#   a sample clean file (only showing the cleaners block)

cleaners:
    AwesomeClean:
        module: mollymaid.cleaners
        devices: [example-device, ]
        timeout: 100
        loglevel: INFO
        retry: 3
# -----------------------------------------------------
# how Kleenex engine internally invokes cleaner classes
# (pseudo code for demonstrating internals only)

# the class is imported first, based on its class name and module info
from mollymaid.cleaners import AwesomeClean

# then the class is instantiated using kwargs from the clean file
# definition (devices/groups keys are ignored: they are consumed by the
# engine instead)
cleaner = AwesomeClean(timeout = 100,
                       loglevel = 'INFO',
                       retry = 3)

# finally, assuming that the testbed is already loaded
# this clean class is invoked to do cleaning on that device
cleaner.clean(device = testbed.devices['example-device'])

Note

During runtime, the kleenex engine actually performs all cleaning in an asynchronous manner: it forks one subprocess per device and cleans all required devices in parallel. This is a framework-level runtime detail, and is not reflected in the above pesudo-code.

In retrospect, cleaner classes have a very intuitive implementation front-end. All the necessary bits and pieces are provided by the engine, and the user is only focused on coding the steps required to get clean done.

Clean Reporting

The results of cleaning for each device is found inside of the results.json file generated at the end of a pyats job or clean. Every device is listed under the KleenexPlugin plugin in Testsuite / Cleansuite / Task plugins depending on the clean scope. Each device will have a result of Passed or Failed and the plugin will have a summary of the results for all devices

The way a cleaner implementation indicates failure is by raising an exception inside its clean API.

Clean Steps

Users may choose to slice a device clean into a series of steps that are individually logged and included in the CleanResultsDetails.yaml report.

If a cleaner’s clean API optionally includes the reserved steps parameter, a step may be created via the steps.start API and used as a context manager. Any exception raised while in the step’s context causes the step to be marked as failed and the clean for the entire device to be marked as failed in the report.

Sample Implementation Without Steps

The following is a pesudo-code implementation of a clean class. The idea is to walk you through on the look & feel of implementing your own cleaners.

# Example
# -------
#
#   a pesudo-code example implementation of a cleaner class

# all clean implementations inherit from BaseCleaner
class ExampleCleaner(BaseCleaner):
    '''BaseCleaner

    demonstrating the details of how cleaners are to be implemented
    '''

    def __init__(self, arg_1, arg_2, arg_3):
        '''__init__

        clean class constructor. All clean initialization arguments should
        be defined as arguments to this method (eg, arg_1, arg_2, arg_3)
        '''

        # always call the parent __init__()
        super().__init__()

        # everything else necessary... eg:
        self.arg_1 = arg_1
        self.arg_2 = arg_2
        self.arg_3 = arg_3

    def clean(self, device):
        '''clean

        main entry point of this clean implementation, this method is called
        in a subprocess under kleenex runtime, specific to cleaning one
        particular device.

        A device object representing the device to clean is always provided.
        '''

        # connect to the device
        device.connect()

        # look up the details of how to clean this device, eg:
        clean_info = device.clean

        # do the actual clean, eg, pseudo-code
        self._imaginary_load_image_method(device = device,
                                          images = clean_info['images'])

        # maybe reload the device?
        self._imaginary_device_reload(device = device)

        # apply post-clean config?
        device.configure(clean_info['post-clean'])

        # we're done!
        device.disconnect()

Sample Implementation With Steps

The preceding clean class could also be implemented by specifying steps. The steps are written into the CleanResultsDetails.yaml file.

# Example
# -------
#
#   a pesudo-code example implementation of a cleaner class

# all clean implementations inherit from BaseCleaner
class ExampleCleanerWithSteps(ExampleCleaner):
    '''BaseCleaner

    demonstrating the use of steps in a cleaner implementation.

    '''

    def clean(self, device, steps):
        '''clean

        main entry point of this clean implementation, this method is called
        in a subprocess under kleenex runtime, specific to cleaning one
        particular device.

        A device object representing the device to clean is always provided.

        A steps object is provided when the ``steps`` parameter is
        specified and may be used to define clean steps that are
        individually logged and tracked in the CleanResultsDetails.yaml
        clean report.
        '''

        # connect to the device
        device.connect()

        # look up the details of how to clean this device, eg:
        clean_info = device.clean

        with steps.start('Clean the device'):
            # do the actual clean, eg, pseudo-code
            self._imaginary_load_image_method(device = device,
                                              images = clean_info['images'])

        with steps.start('Reload the device'):
            # maybe reload the device?
            self._imaginary_device_reload(device = device)

        with steps.start('Apply post-clean configuration'):
            # apply post-clean config?
            device.configure(clean_info['post-clean'])

        # we're done!
        device.disconnect()