From 7c251d4020f64511b91c42f2042d5c084f802f33 Mon Sep 17 00:00:00 2001 From: "omehrabi@cisco.com" Date: Fri, 22 Jul 2022 16:31:20 -0400 Subject: [PATCH 01/61] update the changelog --- docs/changelog/2022/july.rst | 64 ++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 65 insertions(+) create mode 100644 docs/changelog/2022/july.rst diff --git a/docs/changelog/2022/july.rst b/docs/changelog/2022/july.rst new file mode 100644 index 0000000..e1e85dd --- /dev/null +++ b/docs/changelog/2022/july.rst @@ -0,0 +1,64 @@ +July 2022 +========== + +July 21 - Pyats v22.7 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v22.7 + ``pyats.aereport``, v22.7 + ``pyats.aetest``, v22.7 + ``pyats.async``, v22.7 + ``pyats.cisco``, v22.7 + ``pyats.connections``, v22.7 + ``pyats.datastructures``, v22.7 + ``pyats.easypy``, v22.7 + ``pyats.kleenex``, v22.7 + ``pyats.log``, v22.7 + ``pyats.reporter``, v22.7 + ``pyats.results``, v22.7 + ``pyats.robot``, v22.7 + ``pyats.tcl``, v22.7 + ``pyats.topology``, v22.7 + ``pyats.utils``, v22.7 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.topology + * Update schema to correctly reflect peripheral structure + +* pyats.easypy + * Modified MailBot: + * Enabled {runtime} formatting for custom subjects on CLI + * Added `email.subject` configuration option + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* ats.devat + * Add DevAT package to pyats repo + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index f2f7b07..a21faac 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2022/july 2022/june 2022/may 2022/april From ffb845faabcca941f7e9ab196dd1040ba00773a8 Mon Sep 17 00:00:00 2001 From: "omehrabi@cisco.com" Date: Mon, 25 Jul 2022 11:14:12 -0400 Subject: [PATCH 02/61] release_22.7 --- docs/changelog/2022/july.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog/2022/july.rst b/docs/changelog/2022/july.rst index e1e85dd..8f723cd 100644 --- a/docs/changelog/2022/july.rst +++ b/docs/changelog/2022/july.rst @@ -1,7 +1,7 @@ July 2022 ========== -July 21 - Pyats v22.7 +July 26 - Pyats v22.7 ------------------------ From e409b73aae60d4ee71cb4e39a3349af3386bb918 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Fri, 29 Jul 2022 14:49:38 +1200 Subject: [PATCH 03/61] Update docs for keys() markup --- docs/topology/creation.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/topology/creation.rst b/docs/topology/creation.rst index 61ada47..cb381d9 100644 --- a/docs/topology/creation.rst +++ b/docs/topology/creation.rst @@ -109,6 +109,7 @@ references, ``topology.loader`` effectively added this using specific syntax # basic syntax # ------------ # %{..<...>} + # %{..<...>.keys()} # %INTF{logical_interface_name} # %ENV{environment_variable_name} # %CALLABLE{path_to_callable} @@ -122,6 +123,7 @@ references, ``topology.loader`` effectively added this using specific syntax # - use %{ } to denote the begin and end of a markup block # - use . to separate reference path # - use 'self' as first word to reference current device + # - use '.keys()' to get the key values for a path # - The %INTF{ } form causes the logical interface name to be # replaced with the actual interface name from the # device's topology block. @@ -154,6 +156,9 @@ references, ``topology.loader`` effectively added this using specific syntax # reference to arbitrary attribute within this YAML file %{a.b.c} + # reference to the list of keys of this attribute within this YAML file + %{d.e.keys()} + # reference to environment variable from the os # (replaced with actual environment variable name) %ENV{environment_variable_name} From 370c942a1648df9911b20a3d0f89b430002e4eab Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Tue, 2 Aug 2022 14:17:15 +1200 Subject: [PATCH 04/61] Update pyats validate docs for jinja2_config subcommand --- docs/cli/pyats_validate.rst | 70 ++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/docs/cli/pyats_validate.rst b/docs/cli/pyats_validate.rst index 24a89ae..6518bc1 100644 --- a/docs/cli/pyats_validate.rst +++ b/docs/cli/pyats_validate.rst @@ -11,6 +11,7 @@ related input files, formats, and content. Subcommands: datafile validate your YAML-based datafile syntax + jinja2_config validate genie config file jinja2 rendering testbed validate your testbed file yaml syntax General Options: @@ -225,4 +226,71 @@ Example .. tip:: - use ``-q`` to quiet pretty information such as "----" sections and headers. \ No newline at end of file + use ``-q`` to quiet pretty information such as "----" sections and headers. + + + + +pyats validate jinja2_config +---------------------------- + +This subcommand validates the content of your Genie configuration datafile. It +gives developers an opportunity to check if the file is written following +valid YAML syntax and what the Jinja2 rendered output configuration looks like. + +.. code-block:: text + + Usage: + pyats validate jinja2_config [file] [options] + + Description: + Validates the provided genie config file for jinja2 rendering. + + Jinja2_config Options: + [file] Genie config datafile to validate + --testbed-file TESTBED + Testbed file (required) + --devices DEVICES [DEVICES ...] + Devices to render configs for (optional) + --sequence SEQUENCE [SEQUENCE ...] + Sequence(s) to render (optional) + + General Options: + -h, --help Show help + -v, --verbose Give more output, additive up to 3 times. + -q, --quiet Give less output, additive up to 3 times, corresponding to WARNING, ERROR, + and CRITICAL logging levels + +Options +^^^^^^^ + +``[file]`` + The Genie configuration datafile. Can be a file path or a URL to a YAML file + +``--devices`` + Device names to render the configuration for. Only configurations for these devices will be + used to render configurations. + +``--sequence`` + Sequence(s) to render the configuration for. Only the sequences specified will be rendered. + + +Example +^^^^^^^ + + +.. code-block:: text + + $ pyats validate jinja2_config data/config_datafile.yaml --testbed-file data/testbed.yaml --sequence 1 + + Device R1 sequence 1: + interface Loopback0 + no shutdown + ip address 1.1.1.1 255.255.255.255 + + + Device R2 sequence 1: + interface Loopback0 + no shutdown + ip address 2.2.2.2 255.255.255.255 + From e712ca7a3f5e0c59c03d743255ab87deb93e0aab Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Wed, 24 Aug 2022 10:25:55 +1200 Subject: [PATCH 05/61] Update manifest doc for boolean and meta argument handling --- docs/manifest/introduction.rst | 52 ++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/docs/manifest/introduction.rst b/docs/manifest/introduction.rst index a4706d6..053070c 100644 --- a/docs/manifest/introduction.rst +++ b/docs/manifest/introduction.rst @@ -48,7 +48,7 @@ the manifest. Currently, the supported types for the manifest are: * easypy -Only suported script types have an execution runtime that allows +Only supported script types have an execution runtime that allows the manifest to execute the script. @@ -80,11 +80,13 @@ The internal argument structure is translated to the command line argument strin * Arguments may explicitly define dash syntax, e.g `"-key": value` * Argument values are quoted using double quotes, e.g. `val1` will translate to `"val1"` * Arguments specified as a list will translate to `--key "val1" "val2"` - * If the value is `*N`, repeat the argument key N number of times + * If the value is `*N`, repeat the argument key N number of times (e.g. `"-v": "*3"` translates to `-v -v -v`) * If the value is a boolean, leave out the value and only add - the key to the argument string, e.g. `flag: True` translates to `--flag`. - * If the boolean needs to be explicitly added to the argument string, the value - must be explicitly specified as a string, e.g. `key: "True"` + the key to the argument string, e.g. `flag: True` translates to `--flag`. Note: + if the boolean needs to be explicitly added to the argument string, the value + must be explicitly specified as a string, e.g. `key: "True"` (not as a boolean value). + See below for overriding boolean arguments. + * If the `meta` argument is used on the command line, it will be prepended as an additional meta argument. For example, the script arguments defined in the manifest could look like this: @@ -110,6 +112,46 @@ For example running the manifest execution with the above arguments and adding t $ pyats validate manifest job.tem --profile local --testbed-file testbed2.yaml +**Meta argument handling** + +If the `meta` argument is used on the command line, it will be prepended +as an additional meta argument. Note, the value for the `meta` argument provided +via a *profile* argument will override the default value (same as for any other argument). + +.. code-block:: yaml + + arguments: + meta: key1=value1 + +.. code-block:: + + $ pyats validate manifest job.tem --meta key2=value2 + +The above will result in the following command line arguments to be used: + +.. code-block:: + + pyats run job job.py --meta key2=value2 --meta key1=value1 + + +**Overriding boolean arguments** + +You can override boolean arguments by specifying the boolean string value after the argument +on the command line, i.e. `"False"` or `"True"`. + +With below example, the ``no-mail: True`` argument specified in the manifest can be overriden +by specifying the argument on the manifest command line with value `False`. This combination +will result in the `--no-mail` argument *not* to be added to the `pyats run job` command. + +.. code-block:: yaml + + arguments: + no-mail: True + +.. code-block:: shell + + $ pyats validate manifest job.tem --no-mail False + runtimes ~~~~~~~~ From 928e82f8b1efaac59fbdc18cbbd56c29dfcf2b06 Mon Sep 17 00:00:00 2001 From: GerriorL <84335026+GerriorL@users.noreply.github.com> Date: Fri, 26 Aug 2022 14:01:08 -0400 Subject: [PATCH 06/61] Releasing v22.8 --- docs/changelog/2022/august.rst | 76 ++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 77 insertions(+) create mode 100644 docs/changelog/2022/august.rst diff --git a/docs/changelog/2022/august.rst b/docs/changelog/2022/august.rst new file mode 100644 index 0000000..fc3b4be --- /dev/null +++ b/docs/changelog/2022/august.rst @@ -0,0 +1,76 @@ +August 2022 +========== + +August 26 - Pyats v22.8 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v22.8 + ``pyats.aereport``, v22.8 + ``pyats.aetest``, v22.8 + ``pyats.async``, v22.8 + ``pyats.cisco``, v22.8 + ``pyats.connections``, v22.8 + ``pyats.datastructures``, v22.8 + ``pyats.easypy``, v22.8 + ``pyats.kleenex``, v22.8 + ``pyats.log``, v22.8 + ``pyats.reporter``, v22.8 + ``pyats.results``, v22.8 + ``pyats.robot``, v22.8 + ``pyats.tcl``, v22.8 + ``pyats.topology``, v22.8 + ``pyats.utils``, v22.8 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.easypy + * Fixed task-uids filter when using default Task IDs (e.g. Task-2) + +* pyats.manifest + * Add support for boolean overrides with `pyats manifest` CLI command + +* manifest + * combine_cli_args_and_script_arguments + * Handling for any number of uses of the ``--meta`` argument + +* pyats.topology + * Updated testbed.raw_config to contain post-extend, post-markup content. This fixes issues introduced by previous changes when using Genie jinja configure and YAML reference markup. + +* pyats.utils + * Updated schemaengine to support optional advanced datatypes + * Updated yaml loader to support advanced datatypes argument + * Updated yaml loader to store raw/pre/mark/validated/post content + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* ats.cisco + * bussinesstelemetry + * Updated get_paches for collecting package from requirements.txt + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index a21faac..87c9a2d 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2022/august 2022/july 2022/june 2022/may From ae2cca4f692bfdc3a939437c99021b60bfac81d8 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Wed, 7 Sep 2022 14:18:18 +1200 Subject: [PATCH 07/61] Add docs for gTask class --- docs/easypy/jobfile.rst | 44 +++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/docs/easypy/jobfile.rst b/docs/easypy/jobfile.rst index 53c6670..857fcb3 100644 --- a/docs/easypy/jobfile.rst +++ b/docs/easypy/jobfile.rst @@ -242,18 +242,24 @@ Task Class ``Task`` class objects represent the task/testscript being executed in a child process. It is a subclass of Python ``multiprocessing.Process`` class, and -always uses ``multiproessing.get_context('fork')`` to fork and create child -processes. +always uses ``multiprocessing.get_context('fork')`` to fork and create child +processes. The ``gTask`` class object is used for Genie Tasks. .. csv-table:: Task Class Arguments :header: "Argument", "Description" ``testscript``, "testscript to be run in this task" - ``taskid``, "unique task id (defaults to ``Task-#`` where # is an - incrementing number)" + ``taskid``, "unique task id (defaults to ``Task-#`` where # is an incrementing number)" ``runtime``, "easypy runtime object" - ``kwargs``, "any other keyword-arguments to be passed to the testscript - as script parameters" + ``clean_files``, "List of clean files, specific to this task (optional)" + ``logical_testbed_file``, "Path to logical testbed file, specific to this task (optional)" + ``kwargs``, "any other keyword-arguments to be passed to the testscript as script parameters" + +For Genie tasks, the `gTask` class can be used. No testscript should be specified for Genie tasks. + +The ``clean_files`` and ``logical_testbed_File`` arguments are optional arguments +that can be passed if the task uses task specific clean. By default, the job level +clean arguments are used. Like its parent ``Process`` class, instantiating a ``Task`` object does not create the actual child process: the class constructor only sets internal states @@ -350,6 +356,32 @@ each task process. # raise exception raise TimeoutError('Not all tasks finished in 5 minutes!') +Example for Genie tasks with gTask. The ``gTask`` class is imported from the `genie.harness.main` module. + +.. code-block:: python + + # Example + # ------- + # + # job file tasks using gTask() api + + from genie.harness.main import gTask + + def main(runtime): + + # using Task class to create a task object + task_1 = gTask(trigger_datafile='trigger_data.yaml', + config_datafile='config_data.yaml', + subsection_datafile='subsection_data.yaml', + trigger_uids='TestBgp', + taskid='task1') + + # start the task + task_1.start() + + # wait for a max runtime of 60*5 seconds = 5 minutes + task_1.wait(60*5) + Easypy expects all tasks to be finished/terminated when ``main()`` scope is exited (eg, the jobfile finished execution). Therefore, all tasks created and started using ``Task`` class should always be waited for using ``wait()``, and From addb5cb312ee959411eab7130b7eaa994d7d019a Mon Sep 17 00:00:00 2001 From: lgerrior Date: Fri, 23 Sep 2022 15:48:27 -0400 Subject: [PATCH 08/61] Releasing v22.9 --- docs/changelog/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 87c9a2d..e708db9 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2022/september 2022/august 2022/july 2022/june From 87b11df7ef53d49e90ca4dceda217fd272557836 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Fri, 14 Oct 2022 20:33:54 +1300 Subject: [PATCH 09/61] Update YAML markup reference --- docs/topology/creation.rst | 215 +-------------------- docs/utilities/index.rst | 1 + docs/utilities/yaml_markup.rst | 341 +++++++++++++++++++++++++++++++++ 3 files changed, 343 insertions(+), 214 deletions(-) create mode 100644 docs/utilities/yaml_markup.rst diff --git a/docs/topology/creation.rst b/docs/topology/creation.rst index cb381d9..201ded3 100644 --- a/docs/topology/creation.rst +++ b/docs/topology/creation.rst @@ -94,221 +94,8 @@ unanimous for all. Testbed File Markups -------------------- -Another advantage of using testbed files is to leverage the built-in markup -feature. Whereas standard YAML format does not allow variable substitution and -references, ``topology.loader`` effectively added this using specific syntax -(markups) similar to the Django template language. +See :ref:`yaml_file_markup` -.. code-block:: text - - # Syntax - # ------ - # - # YAML markup syntax - - # basic syntax - # ------------ - # %{..<...>} - # %{..<...>.keys()} - # %INTF{logical_interface_name} - # %ENV{environment_variable_name} - # %CALLABLE{path_to_callable} - # %CALLABLE{path_to_callable(param1,param2,param3)} - # %INCLUDE{yaml_file_path} - # %ASK{optional prompt text} - # %ENC{encoded text} - # %ENC{encoded text, prefix=x} - # %CLI{cli_argument_name} - # - # - use %{ } to denote the begin and end of a markup block - # - use . to separate reference path - # - use 'self' as first word to reference current device - # - use '.keys()' to get the key values for a path - # - The %INTF{ } form causes the logical interface name to be - # replaced with the actual interface name from the - # device's topology block. - # - The %ENV{ } form causes the environment variable name to be - # replaced with the actual environment value from the os. - # - The %CALLABLE{ } form causes the callable to be replaced with the - # actual return value from the callable. All defined parameters - # will be passed to the callable. - # - The %INCLUDE{ } form causes the yaml file path to be replaced - # with the actual content of the yaml file. - # - The %ASK{ } form causes the user to be prompted to enter information - # manually. - # - The %ENC{ } form causes an encoded string to be replaced with a - # decoded string or secret string which supports decoding. - # - The %CLI{ } form replaces the variable name with the value provided - # from the command line argument. If no command line argument was - # provided for this variable, the value will be an empty string. - # Supports single and double dash argument style. - - # reference to current device name - %{self} - - # reference to attributes within current device - %{self.x.y.z} - - # reference to logical interface within current device - # (replaced with actual interface name) - %INTF{logical_interface_name} - - # reference to arbitrary attribute within this YAML file - %{a.b.c} - - # reference to the list of keys of this attribute within this YAML file - %{d.e.keys()} - - # reference to environment variable from the os - # (replaced with actual environment variable name) - %ENV{environment_variable_name} - - # reference to callable without parameter - # (replaced with actual path to callable) - %CALLABLE{path.to.callable} - - # reference to callable with parameters param1, param2 and param3 - # (replaced with actual path to callable) - %CALLABLE{path.to.callable(param1,param2,param3)} - - # reference to content from other YAML file - # (replaced with actual path to YAML file) - %INCLUDE{yaml_file_path} - - # prompt user to enter string content manually - %ASK{optional prompt text} - - # Reference to text encoded with "pyats secret encode" command - # Encoded credential passwords are substituted by secret strings. - # Other encoded references are substituted with their decoded string. - # See secret strings documentation for details. - %ENC{} - - # Reference to text encoded with "pyats secret encode --prefix x" command. - # Encoded credential passwords are substituted by secret strings. - # Other encoded references are substituted with their decoded string. - # See secret strings documentation for details. - %ENC{, prefix=x} - - # Reference to "some_arg" will be replaced by "some_value" if - # the command line "pyats run job --some_arg some_value" is used. - %CLI{some_arg} - - # If the command line argument is provided without a value, - # the value is set to boolean 'True'. The following command line - # sets the value for "some_flag" to True. - # "pyats run job --some_flag" - %CLI{some_flag} - - # If the command line argument has multiple values, - # the variable is replaced with a list of values. - # The following command line argument creates a list - # of values in place of the devices variable. - # "pyats run job --devices R1 R2" - %CLI{devices} - - # If the command line argument contains a number value, - # either integer or float, the variable is converted from - # a string to an integer or float. - # "pyats run job --retries 3" - %CLI{retries} - -.. note:: - - Make sure to enclose your markup in quotes if it occurs directly - after a colon. For example:: - - testbed: - name: my_testbed - - passwords: - enable: lab - line: "%{testbed.passwords.enable}" - tacacs: "%{testbed.passwords.enable}" - tacacs: - username: admin - -YAML itself does not distinguish the markups from regular text (strings). -Before the creation of testbed objects, the loader walks through the generated -data and replaces all markup languages with referenced data. -Any syntax outside of the above is neither recognized nor processed. - -.. code-block:: yaml - - # Example - # ------- - # - # yaml testbed using markup - # notice how markups were used as information references. - devices: - example_device: - type: "%CALLABLE{mylib.get_device_type}" - connections: - a: - protocol: telnet - ip: "1.1.1.1" - port: 2001 - alt: - protocol: telnet - ip: "%{self.clean.mgt_itf.ipv4.address}" - - dynamic_device: "%CALLABLE{mylib.create_device(2.2.2.2)}" - topology: - example_device: - interfaces: - Ethernet4/6: - alias: my_logical_interface - link: link-x - type: "%ENV{DEFAULT_INTERFACE_TYPE}" - dynamic_device: "%INCLUDE{/path/to/dynamic/generated/device/interfaces/file}" - - -Testbed file can be broken down in multiple yaml files with the extend key. -Each file can represent a subset of the main testbed file. - - -Let's say this file is named tb1.yaml - -.. code-block:: yaml - - devices: - xr-1: - connections: - cli: - ip: 10.1.1.1 - protocol: ssh - credentials: - default: - password: cisco - username: cisco - enable: - password: cisco - os: iosxr - type: iosxr - - -And this file is named tb2.yaml - -.. code-block:: yaml - - extends: tb1.yaml - devices: - xr-2: - connections: - cli: - ip: 10.2.2.2 - protocol: ssh - credentials: - default: - password: cisco - username: cisco - enable: - password: cisco - os: iosxr - type: iosxr - -Now at run time, you can provide the tb2.yaml, which will merge tb1.yaml and -tb2.yaml together to create a merged testbed. Manual Creation --------------- diff --git a/docs/utilities/index.rst b/docs/utilities/index.rst index 895bd42..eeacdd2 100644 --- a/docs/utilities/index.rst +++ b/docs/utilities/index.rst @@ -11,5 +11,6 @@ Utilities file_transfer_utilities file_transfer_server import_utils + yaml_markup .. sectionauthor:: Myles Dear diff --git a/docs/utilities/yaml_markup.rst b/docs/utilities/yaml_markup.rst new file mode 100644 index 0000000..13c7db0 --- /dev/null +++ b/docs/utilities/yaml_markup.rst @@ -0,0 +1,341 @@ + +.. _yaml_file_markup: + +YAML File Markups +================= + +The pyATS YAML loader, which is used in several places including the testbed +loader, supports pyATS specific YAML markup. + +Whereas standard YAML format does not allow variable substitution and +references, ``pyats.utils.yaml.Loader`` effectively added this using specific +syntax (markups) similar to the Django template language. + +Reference +--------- + +.. code-block:: text + + # Syntax + # ------ + # + # YAML markup syntax + + # basic syntax + # ------------ + # %{..<...>} + # %{..<...>.keys()} + # %INTF{logical_interface_name} + # %ENV{environment_variable_name} + # %CALLABLE{path_to_callable} + # %CALLABLE{path_to_callable(param1,param2,param3)} + # %INCLUDE{yaml_file_path} + # %ASK{optional prompt text} + # %ENC{encoded text} + # %ENC{encoded text, prefix=x} + # %CLI{cli_argument_name} + # %EXTEND_LIST{key} + # %EXTEND_LIST{path.to.value1,path.to.value2} + # + # - use %{ } to denote the begin and end of a markup block + # - use . to separate reference path + # - use 'self' as first word to reference current device + # - use '.keys()' to get the key values for a path + # - The %INTF{ } form causes the logical interface name to be + # replaced with the actual interface name from the + # device's topology block. + # - The %ENV{ } form causes the environment variable name to be + # replaced with the actual environment value from the os. + # - The %CALLABLE{ } form causes the callable to be replaced with the + # actual return value from the callable. All defined parameters + # will be passed to the callable. + # - The %INCLUDE{ } form causes the yaml file path to be replaced + # with the actual content of the yaml file. + # - The %ASK{ } form causes the user to be prompted to enter information + # manually. + # - The %ENC{ } form causes an encoded string to be replaced with a + # decoded string or secret string which supports decoding. + # - The %CLI{ } form replaces the variable name with the value provided + # from the command line argument. If no command line argument was + # provided for this variable, the value will be an empty string. + # Supports single and double dash argument style. + # - The %EXTEND_LIST{ } form can be used for keys to extend a list + # from another YAML file. The same syntax can also be used to create + # a value by extending one or more list references. + + +Markup Examples +--------------- + +.. code-block:: text + + # reference to current device name + %{self} + + # reference to attributes within current device + %{self.x.y.z} + + # reference to logical interface within current device + # (replaced with actual interface name) + %INTF{logical_interface_name} + + # reference to arbitrary attribute within this YAML file + %{a.b.c} + + # reference to the list of keys of this attribute within this YAML file + %{d.e.keys()} + + # reference to environment variable from the os + # (replaced with actual environment variable name) + %ENV{environment_variable_name} + + # reference to callable without parameter + # (replaced with actual path to callable) + %CALLABLE{path.to.callable} + + # reference to callable with parameters param1, param2 and param3 + # (replaced with actual path to callable) + %CALLABLE{path.to.callable(param1,param2,param3)} + + # reference to content from other YAML file + # (replaced with actual path to YAML file) + %INCLUDE{yaml_file_path} + + # prompt user to enter string content manually + %ASK{optional prompt text} + + # Reference to text encoded with "pyats secret encode" command + # Encoded credential passwords are substituted by secret strings. + # Other encoded references are substituted with their decoded string. + # See secret strings documentation for details. + %ENC{} + + # Reference to text encoded with "pyats secret encode --prefix x" command. + # Encoded credential passwords are substituted by secret strings. + # Other encoded references are substituted with their decoded string. + # See secret strings documentation for details. + %ENC{, prefix=x} + + # Reference to "some_arg" will be replaced by "some_value" if + # the command line "pyats run job --some_arg some_value" is used. + %CLI{some_arg} + + # If the command line argument is provided without a value, + # the value is set to boolean 'True'. The following command line + # sets the value for "some_flag" to True. + # "pyats run job --some_flag" + %CLI{some_flag} + + # If the command line argument has multiple values, + # the variable is replaced with a list of values. + # The following command line argument creates a list + # of values in place of the devices variable. + # "pyats run job --devices R1 R2" + %CLI{devices} + + # If the command line argument contains a number value, + # either integer or float, the variable is converted from + # a string to an integer or float. + # "pyats run job --retries 3" + %CLI{retries} + +.. note:: + + Make sure to enclose your markup in quotes if it occurs directly + after a colon. For example:: + + testbed: + name: my_testbed + + passwords: + enable: lab + line: "%{testbed.passwords.enable}" + tacacs: "%{testbed.passwords.enable}" + tacacs: + username: admin + +YAML itself does not distinguish the markups from regular text (strings). +Before the creation of testbed objects, the loader walks through the generated +data and replaces all markup languages with referenced data. +Any syntax outside of the above is neither recognized nor processed. + + +Testbed YAML Examples +--------------------- + +.. code-block:: yaml + + # Example + # ------- + # + # yaml testbed using markup + # notice how markups were used as information references. + devices: + example_device: + type: "%CALLABLE{mylib.get_device_type}" + connections: + a: + protocol: telnet + ip: "1.1.1.1" + port: 2001 + alt: + protocol: telnet + ip: "%{self.clean.mgt_itf.ipv4.address}" + + dynamic_device: "%CALLABLE{mylib.create_device(2.2.2.2)}" + topology: + example_device: + interfaces: + Ethernet4/6: + alias: my_logical_interface + link: link-x + type: "%ENV{DEFAULT_INTERFACE_TYPE}" + dynamic_device: "%INCLUDE{/path/to/dynamic/generated/device/interfaces/file}" + + +Testbed file can be broken down in multiple yaml files with the extend key. +Each file can represent a subset of the main testbed file. + + +Let's say this file is named tb1.yaml + +.. code-block:: yaml + + devices: + xr-1: + connections: + cli: + ip: 10.1.1.1 + protocol: ssh + credentials: + default: + password: cisco + username: cisco + enable: + password: cisco + os: iosxr + type: iosxr + + +And this file is named tb2.yaml + +.. code-block:: yaml + + extends: tb1.yaml + devices: + xr-2: + connections: + cli: + ip: 10.2.2.2 + protocol: ssh + credentials: + default: + password: cisco + username: cisco + enable: + password: cisco + os: iosxr + type: iosxr + +Now at run time, you can provide the tb2.yaml, which will merge tb1.yaml and +tb2.yaml together to create a merged testbed. + +EXTEND_LIST usage examples +-------------------------- + +`%EXTEND_LIST` markup can be used in two ways: as a key markup or as a value markup. + +1. Example EXTEND_LIST usage for key markup. + +Example YAML file to be extended: + +.. code-block:: yaml + + parameters: + sections: [a, b] + +YAML file extending the file above: + +.. code-block:: yaml + + extends: a.yaml + + parameters: + "%EXTEND_LIST{sections}": [c] + +Example code loading an extended YAML file: + +.. code-block:: python + + from pyats.utils.yaml import Loader + loader = Loader(enable_extensions=True) + data = loader.load('b.yaml') + print(data) + +The output from above script is show below. As you can see, +the list data was extended from [a, b] to [a, b, c]. + +.. code-block:: text + + {'parameters': {'sections': ['a', 'b', 'c']}} + + +2. Example EXTEND_LIST usage for values. + +It's also possible to created a list from one or more other lists using +the EXTEND_LIST markup with one or more references to list values. + +.. code-block:: yaml + + parameters: + base_config: + CE1: + bgp: + address_families: + ipv4: + neighbors: + 1.1.1.1: {} + 1.1.1.2: {} + ipv6: + neighbors: + - 1::1 + - 1::2 + + CE1_neighbors: "%EXTEND_LIST{parameters.base_config.CE1.bgp.address_families.ipv4.neighbors.keys(),parameters.base_config.CE1.bgp.address_families.ipv6.neighbors}" + + +The output from above data after loading is shown below. The single lists of +neighbors is created by combining the two lists using their markup reference. + +.. code-block:: text + + { + "parameters": { + "base_config": { + "CE1": { + "bgp": { + "address_families": { + "ipv4": { + "neighbors": { + "1.1.1.1": {}, + "1.1.1.2": {} + } + }, + "ipv6": { + "neighbors": [ + "1::1", + "1::2" + ] + } + } + } + } + }, + "CE1_neighbors": [ + "1.1.1.1", + "1.1.1.2", + "1::1", + "1::2" + ] + } + } From f6920a54583ff1077f4eb49cc4a0371447febeba Mon Sep 17 00:00:00 2001 From: lgerrior Date: Fri, 21 Oct 2022 19:45:49 -0400 Subject: [PATCH 10/61] Releasing v22.10 --- docs/changelog/2022/october.rst | 64 +++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 65 insertions(+) create mode 100644 docs/changelog/2022/october.rst diff --git a/docs/changelog/2022/october.rst b/docs/changelog/2022/october.rst new file mode 100644 index 0000000..cfc27fb --- /dev/null +++ b/docs/changelog/2022/october.rst @@ -0,0 +1,64 @@ +October 2022 +========== + +October 25 - Pyats v22.10 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v22.10 + ``pyats.aereport``, v22.10 + ``pyats.aetest``, v22.10 + ``pyats.async``, v22.10 + ``pyats.cisco``, v22.10 + ``pyats.connections``, v22.10 + ``pyats.datastructures``, v22.10 + ``pyats.easypy``, v22.10 + ``pyats.kleenex``, v22.10 + ``pyats.log``, v22.10 + ``pyats.reporter``, v22.10 + ``pyats.results``, v22.10 + ``pyats.robot``, v22.10 + ``pyats.tcl``, v22.10 + ``pyats.topology``, v22.10 + ``pyats.utils``, v22.10 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.utils + * Updated markup processer to support keys() as target + * Support %EXTEND_LIST markup to extend one or more lists as a value + * Modified recursive dict update to support hierarchical use of EXTEND_LIST markup + +* log + * Modified archive.py + * Added await to fix the bug where exposed API calls for results does not return file contents. + +* reporter + * Added Schema + * schema for results.json + * Modified commands.py + * added pyats validate results + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index e708db9..fe40bcc 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2022/october 2022/september 2022/august 2022/july From a6781996dba4eb602d4cbfe6bb8c0c073abd0bbe Mon Sep 17 00:00:00 2001 From: omid Date: Tue, 29 Nov 2022 12:53:18 -0500 Subject: [PATCH 11/61] update changelog --- docs/changelog/2022/november.rst | 52 ++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 53 insertions(+) create mode 100644 docs/changelog/2022/november.rst diff --git a/docs/changelog/2022/november.rst b/docs/changelog/2022/november.rst new file mode 100644 index 0000000..33d5f71 --- /dev/null +++ b/docs/changelog/2022/november.rst @@ -0,0 +1,52 @@ +November 2022 +========== + +November 28 - Pyats v22.11 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v22.11 + ``pyats.aereport``, v22.11 + ``pyats.aetest``, v22.11 + ``pyats.async``, v22.11 + ``pyats.cisco``, v22.11 + ``pyats.connections``, v22.11 + ``pyats.datastructures``, v22.11 + ``pyats.easypy``, v22.11 + ``pyats.kleenex``, v22.11 + ``pyats.log``, v22.11 + ``pyats.reporter``, v22.11 + ``pyats.results``, v22.11 + ``pyats.robot``, v22.11 + ``pyats.tcl``, v22.11 + ``pyats.topology``, v22.11 + ``pyats.utils``, v22.11 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.cisco + * Fix TIMS upload (Cisco internal use only) + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index fe40bcc..35c0e90 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2022/november 2022/october 2022/september 2022/august From 145164ad75c9d92b4dc7b8689f599c6dacfc4c36 Mon Sep 17 00:00:00 2001 From: GerriorL <84335026+gerriorl@users.noreply.github.com> Date: Tue, 31 Jan 2023 13:29:22 -0500 Subject: [PATCH 12/61] Releasing v23.1 --- docs/changelog/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 35c0e90..eff7cc9 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/january 2022/november 2022/october 2022/september From ef6383a509a39fcd4050b77bd02d572dbbdcb13d Mon Sep 17 00:00:00 2001 From: omid Date: Tue, 31 Jan 2023 14:21:37 -0500 Subject: [PATCH 13/61] update documentation --- docs/utilities/yaml_markup.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/utilities/yaml_markup.rst b/docs/utilities/yaml_markup.rst index 13c7db0..5e0a89d 100644 --- a/docs/utilities/yaml_markup.rst +++ b/docs/utilities/yaml_markup.rst @@ -153,6 +153,16 @@ Markup Examples tacacs: "%{testbed.passwords.enable}" tacacs: username: admin +.. note:: + + Make sure to enclose strings with % sign in quotes + in the yaml file. For example:: + + testbed: + name: my_testbed + + credentials: + password: "%ASK{Your password}" YAML itself does not distinguish the markups from regular text (strings). Before the creation of testbed objects, the loader walks through the generated From c267a12dd8fd76968f5777dd675927a776fe152e Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Fri, 17 Feb 2023 12:21:58 +0100 Subject: [PATCH 14/61] Update docs for CLI markup --- docs/utilities/yaml_markup.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/utilities/yaml_markup.rst b/docs/utilities/yaml_markup.rst index 5e0a89d..0858b72 100644 --- a/docs/utilities/yaml_markup.rst +++ b/docs/utilities/yaml_markup.rst @@ -34,6 +34,8 @@ Reference # %ENC{encoded text} # %ENC{encoded text, prefix=x} # %CLI{cli_argument_name} + # %CLI{another_cli_arg, default=} + # %CLI{another_list, default=[,]} # %EXTEND_LIST{key} # %EXTEND_LIST{path.to.value1,path.to.value2} # @@ -58,7 +60,9 @@ Reference # - The %CLI{ } form replaces the variable name with the value provided # from the command line argument. If no command line argument was # provided for this variable, the value will be an empty string. - # Supports single and double dash argument style. + # Supports single and double dash argument style. A default value + # can be specified using `, default=` syntax. A list can be specified + # as default value. # - The %EXTEND_LIST{ } form can be used for keys to extend a list # from another YAML file. The same syntax can also be used to create # a value by extending one or more list references. @@ -139,6 +143,12 @@ Markup Examples # "pyats run job --retries 3" %CLI{retries} + # A default value can be specified for the %CLI markup. + %CLI{another_flag, default=12} + + # A default value can be a list + %CLI{another_flag, default=[1,2]} + .. note:: Make sure to enclose your markup in quotes if it occurs directly From 35eb4c0fc06e185988607c1b9429d778c181fee1 Mon Sep 17 00:00:00 2001 From: "Ben Astell (bastell)" Date: Thu, 23 Feb 2023 10:33:01 -0500 Subject: [PATCH 15/61] new migrate command --- docs/cli/index.rst | 1 + docs/cli/pyats.rst | 14 ++++++++++-- docs/cli/pyats_migrate.rst | 44 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 docs/cli/pyats_migrate.rst diff --git a/docs/cli/index.rst b/docs/cli/index.rst index 5ed5993..f4ec086 100644 --- a/docs/cli/index.rst +++ b/docs/cli/index.rst @@ -15,6 +15,7 @@ and usage. pyats_create pyats_develop pyats_logs + pyats_migrate pyats_run pyats_secret pyats_shell diff --git a/docs/cli/pyats.rst b/docs/cli/pyats.rst index 8cb59fc..2721dbe 100644 --- a/docs/cli/pyats.rst +++ b/docs/cli/pyats.rst @@ -14,8 +14,18 @@ Synopsis pyats [options] Commands: - validate utlities that helps to validate input files - version display currently installed pyATS version + clean runs the provided clean file + create create scripts and libraries from template + diff Command to diff two snapshots saved to file or directory + learn Command to learn device features and save to file + logs command enabling log archive viewing in local browser + migrate utilities for migrating to future versions of pyATS + parse Command to parse show commands + run runs the provided script and output corresponding results. + secret utilities for working with secret strings. + shell enter Python shell, loading a pyATS testbed file and/or pickled data + validate utilities that help to validate input files + version commands related to version display and manipulation General Options: -h, --help Show help diff --git a/docs/cli/pyats_migrate.rst b/docs/cli/pyats_migrate.rst new file mode 100644 index 0000000..973de7c --- /dev/null +++ b/docs/cli/pyats_migrate.rst @@ -0,0 +1,44 @@ +pyats migrate +============= + +Command to identify and inform a user of required upcoming changes to their +environment. + +.. code-block:: text + + Usage: + pyats migrate [options] + + Subcommands: + abstract Discover what changes must be made to existing test environments to conform + to the new token discovery. + + +pyats migrate abstract +--------------------- + +The mechanism for `Genie Abstract`_ and the organization of `Unicon Supported Platforms`_ +are being updated for better consistency and utility. This subcommand will +examine your environment and inform you of any potential changes that may be +required. The usage of ``os``, ``model``, ``platform`` will be strictly defined within +Unicon in the `PID tokens`_ file and may require updates to testbeds and scripts to correctly reflect +these definitions. + +.. _Genie Abstract: https://pubhub.devnetcloud.com/media/genie-docs/docs/abstract/index.html +.. _Unicon Supported Platforms: https://pubhub.devnetcloud.com/media/unicon/docs/user_guide/supported_platforms.html +.. _PID tokens: https://github.com/CiscoTestAutomation/unicon.plugins/blob/master/src/unicon/plugins/pid_tokens.csv + +.. code-block:: text + + Usage: + pyats migrate abstract [options] + + Description: + Discover what changes must be made to existing test environments to conform to the new token discovery. + + Abstract Options: + path Path to start searching for files + --init Only process __init__.py files + --python Only process Python files + --yaml Only process YAML files + --threads THREADS Max number of threads to run while processing files (default 100) From f82bf28c832540e02f723f3fddabbe4210ab279c Mon Sep 17 00:00:00 2001 From: lsheikal Date: Fri, 24 Feb 2023 13:00:16 -0800 Subject: [PATCH 16/61] Releasing v23.2 --- docs/changelog/2023/february.rst | 60 ++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 61 insertions(+) create mode 100644 docs/changelog/2023/february.rst diff --git a/docs/changelog/2023/february.rst b/docs/changelog/2023/february.rst new file mode 100644 index 0000000..d6b1717 --- /dev/null +++ b/docs/changelog/2023/february.rst @@ -0,0 +1,60 @@ +February 2023 +========== + +February 24 - Pyats v23.2 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.2 + ``pyats.aereport``, v23.2 + ``pyats.aetest``, v23.2 + ``pyats.async``, v23.2 + ``pyats.cisco``, v23.2 + ``pyats.connections``, v23.2 + ``pyats.datastructures``, v23.2 + ``pyats.easypy``, v23.2 + ``pyats.kleenex``, v23.2 + ``pyats.log``, v23.2 + ``pyats.reporter``, v23.2 + ``pyats.results``, v23.2 + ``pyats.robot``, v23.2 + ``pyats.tcl``, v23.2 + ``pyats.topology``, v23.2 + ``pyats.utils``, v23.2 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats.utils + * Modified %CLI markup + * Added support for default value, including list syntax support + +* install + * Updated the pyats installer. + * The installer can now install all the required and optional packages. + +* topology + * Updated the schema to support management apis + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index eff7cc9..a701351 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/february 2023/january 2022/november 2022/october From 51fd1179794a8d27cdd667f28efa83b75ac60c68 Mon Sep 17 00:00:00 2001 From: lsheikal Date: Fri, 3 Mar 2023 11:45:56 -0800 Subject: [PATCH 17/61] updated changelogs --- docs/changelog/2022/september.rst | 87 +++++++++++++++++++++++++++++++ docs/changelog/2023/february.rst | 2 +- docs/changelog/2023/january.rst | 46 ++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 docs/changelog/2022/september.rst create mode 100644 docs/changelog/2023/january.rst diff --git a/docs/changelog/2022/september.rst b/docs/changelog/2022/september.rst new file mode 100644 index 0000000..f67031f --- /dev/null +++ b/docs/changelog/2022/september.rst @@ -0,0 +1,87 @@ +September 2022 +========== + +September 27 - Pyats v22.9 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v22.9 + ``pyats.aereport``, v22.9 + ``pyats.aetest``, v22.9 + ``pyats.async``, v22.9 + ``pyats.cisco``, v22.9 + ``pyats.connections``, v22.9 + ``pyats.datastructures``, v22.9 + ``pyats.easypy``, v22.9 + ``pyats.kleenex``, v22.9 + ``pyats.log``, v22.9 + ``pyats.reporter``, v22.9 + ``pyats.results``, v22.9 + ``pyats.robot``, v22.9 + ``pyats.tcl``, v22.9 + ``pyats.topology``, v22.9 + ``pyats.utils``, v22.9 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + + +* pyats.topology + * Fixed return exit code for pyats validate testbed + +* pyats installer + * Updated pyATS installer + * Pinned installer dependencies + * Refactor installer builder + +* pyats.cli + * Updated cmd implementation, added encoding and ignoring errors + * Log output of failed commands if available + +* pyats.easypy + * Add support for clean files and logical testbed per task + +* easypy + * Modified Task + * Improved terminate to kill any subprocesses that remain after attempting to terminate + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats + * Pinned pip version to use pip index versions + * Added wrapper for required, legacy and optional packages + +* pyats.cli.commands.version + * Added CheckAvailableVersions + * Created method to get all available versions of a package + * Created method to get the latest version of a list of packages + +* Modified update + * Added latest version to dialog confirming which packages will be updated + * Added --pre argument to fetch minor and dev versions + diff --git a/docs/changelog/2023/february.rst b/docs/changelog/2023/february.rst index d6b1717..11ce6bb 100644 --- a/docs/changelog/2023/february.rst +++ b/docs/changelog/2023/february.rst @@ -1,7 +1,7 @@ February 2023 ========== -February 24 - Pyats v23.2 +February 28 - Pyats v23.2 ------------------------ diff --git a/docs/changelog/2023/january.rst b/docs/changelog/2023/january.rst new file mode 100644 index 0000000..c8b281a --- /dev/null +++ b/docs/changelog/2023/january.rst @@ -0,0 +1,46 @@ +January 2023 +========== + +January 31 - Pyats v23.1 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.1 + ``pyats.aereport``, v23.1 + ``pyats.aetest``, v23.1 + ``pyats.async``, v23.1 + ``pyats.cisco``, v23.1 + ``pyats.connections``, v23.1 + ``pyats.datastructures``, v23.1 + ``pyats.easypy``, v23.1 + ``pyats.kleenex``, v23.1 + ``pyats.log``, v23.1 + ``pyats.reporter``, v23.1 + ``pyats.results``, v23.1 + ``pyats.robot``, v23.1 + ``pyats.tcl``, v23.1 + ``pyats.topology``, v23.1 + ``pyats.utils``, v23.1 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ + + From f5d6ea37e09c5aa262a2aad1413568f12e46d1de Mon Sep 17 00:00:00 2001 From: "Ben Astell (bastell)" Date: Thu, 30 Mar 2023 10:35:05 -0400 Subject: [PATCH 18/61] changelogs 23.3 --- docs/changelog/2023/march.rst | 55 +++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 56 insertions(+) create mode 100644 docs/changelog/2023/march.rst diff --git a/docs/changelog/2023/march.rst b/docs/changelog/2023/march.rst new file mode 100644 index 0000000..09be568 --- /dev/null +++ b/docs/changelog/2023/march.rst @@ -0,0 +1,55 @@ +March 2023 +========== + +March 28 - Pyats v23.3 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.3 + ``pyats.aereport``, v23.3 + ``pyats.aetest``, v23.3 + ``pyats.async``, v23.3 + ``pyats.cisco``, v23.3 + ``pyats.connections``, v23.3 + ``pyats.datastructures``, v23.3 + ``pyats.easypy``, v23.3 + ``pyats.kleenex``, v23.3 + ``pyats.log``, v23.3 + ``pyats.reporter``, v23.3 + ``pyats.results``, v23.3 + ``pyats.robot``, v23.3 + ``pyats.tcl``, v23.3 + ``pyats.topology``, v23.3 + ``pyats.utils``, v23.3 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.manifest + * Add support for CLI arguments with * as value + +* pyats.utils + * Fixed default value with CLI markup usage + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index a701351..efc56bf 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/march 2023/february 2023/january 2022/november From d9d6d1f70a7acb3ee384a2fb681b05c2c10b92b6 Mon Sep 17 00:00:00 2001 From: "Ben Astell (bastell)" Date: Tue, 25 Apr 2023 17:18:41 -0400 Subject: [PATCH 19/61] 23.4 changelogs --- docs/changelog/2023/april.rst | 73 +++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 74 insertions(+) create mode 100644 docs/changelog/2023/april.rst diff --git a/docs/changelog/2023/april.rst b/docs/changelog/2023/april.rst new file mode 100644 index 0000000..d626248 --- /dev/null +++ b/docs/changelog/2023/april.rst @@ -0,0 +1,73 @@ +April 2023 +========== + +April 25 - Pyats v23.4 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.4 + ``pyats.aereport``, v23.4 + ``pyats.aetest``, v23.4 + ``pyats.async``, v23.4 + ``pyats.cisco``, v23.4 + ``pyats.connections``, v23.4 + ``pyats.datastructures``, v23.4 + ``pyats.easypy``, v23.4 + ``pyats.kleenex``, v23.4 + ``pyats.log``, v23.4 + ``pyats.reporter``, v23.4 + ``pyats.results``, v23.4 + ``pyats.robot``, v23.4 + ``pyats.tcl``, v23.4 + ``pyats.topology``, v23.4 + ``pyats.utils``, v23.4 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.manifest + * Add support for CLI arguments with * as value + +* pyats.utils + * Fixed default value with CLI markup usage + +* pyats.easypy + * Update the plugin loading in the plugins and update the black_box test. + +* install + * Fix for installer to source venv for pip format json command. + +* update + * Fix to pick up ats packages instead of pyats + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* easypy + * moved AEPluginReporter and AEPluginContext and report_func Wrapper from Plugin Bundle + * move the reporter from plugin bundle to easypy reporter + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index efc56bf..c5ea5f2 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/april 2023/march 2023/february 2023/january From 10ea6946db06f126043f385a14814b9222d47ff3 Mon Sep 17 00:00:00 2001 From: GerriorL <84335026+gerriorl@users.noreply.github.com> Date: Wed, 24 May 2023 17:14:53 -0400 Subject: [PATCH 20/61] Update docs for incoming changes --- docs/aetest/debugging.rst | 44 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/aetest/debugging.rst b/docs/aetest/debugging.rst index 0224720..0bed168 100644 --- a/docs/aetest/debugging.rst +++ b/docs/aetest/debugging.rst @@ -227,8 +227,9 @@ want to pause the testscript execution somewhere & check the current state and/or configuration of your environment, logs, and testbed devices? The **pause on phrase** feature allows you to pause on **any** log messages -generated by the current script run, without requiring any modifications to the -scripts and/or its libraries. When enabled, it actively filters all log messages +generated by the current script run, including CLI output from devices, +without requiring any modifications to the scripts and/or its libraries. +When enabled, it actively filters all log messages propagated to the ``logging.root`` logger, and pauses when a match is found. Three distinct types of pause actions are supported: @@ -486,3 +487,42 @@ Below are some examples of the pause on phrase feature in action: # -> /path/to/my/example/testscript.py[20] test() # (press Ctrl-D to resume execution) # >>> + +The pause will also dump the connection information, including which devices are currently connected. + +.. code-block:: python + + # -------------------------------------------------------------------------------- + # Pause On Phrase: Connection Information + # + # +-----------------+--------------------------------+ + # | Device Property | Value | + # +-----------------+--------------------------------+ + # | Name | device.name | + # | Alias | device.alias | + # | Status | "Connected"/"Disconnected" | + # | Class | connections.connection.class | + # | IP | connection.connection.ip | + # | Port | connection.connection.port | + # | Protocol | connection.connection.protocol | + # | -------- | -------- | + # | URL | ://: | + # | Spawn Command | device.spawn.spawn_command | + # +-----------------+--------------------------------+ + + +If it is required to connect to the device directly while the test is paused, the device connection must be disconnected +from before it can be accessed directly. + +.. code-block:: python + + >>> import pyats.easypy as ep + >>> dev = ep.runtime.testbed.devices.uut + >>> dev.disconnect() + + # the device can now be connected to in a separate terminal. Once ready to resume the test, + # break the connection and reattach with + + >>> dev.connect() + + From f1e77c65d53cf79170e7e7c151a45d8161a55890 Mon Sep 17 00:00:00 2001 From: GerriorL <84335026+gerriorl@users.noreply.github.com> Date: Thu, 25 May 2023 09:58:38 -0400 Subject: [PATCH 21/61] Update table --- docs/aetest/debugging.rst | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/aetest/debugging.rst b/docs/aetest/debugging.rst index 0bed168..87360e8 100644 --- a/docs/aetest/debugging.rst +++ b/docs/aetest/debugging.rst @@ -495,20 +495,22 @@ The pause will also dump the connection information, including which devices are # -------------------------------------------------------------------------------- # Pause On Phrase: Connection Information # - # +-----------------+--------------------------------+ - # | Device Property | Value | - # +-----------------+--------------------------------+ - # | Name | device.name | - # | Alias | device.alias | - # | Status | "Connected"/"Disconnected" | - # | Class | connections.connection.class | - # | IP | connection.connection.ip | - # | Port | connection.connection.port | - # | Protocol | connection.connection.protocol | - # | -------- | -------- | - # | URL | ://: | - # | Spawn Command | device.spawn.spawn_command | - # +-----------------+--------------------------------+ + # +-------------------+--------------------------------+ + # | Device Property | Value | + # +-------------------+--------------------------------+ + # | Name | device.name | + # | Alias | device.alias | + # | Active Connection | alias of active connection | + # | Status | Connected|Disconnected | + # | Spawn Command | command for active connection | + # | -------- | -------- | + # | Connection Alias | connection alias e.g. cli | + # | Class | Unicon, Gnmi, Netconf, etc | + # | IP | IPV4|IPV6 address | + # | Protocol | ssh, telnet, etc | + # | -------- | -------- | + # | URL | ://: | + # +-------------------+--------------------------------+ If it is required to connect to the device directly while the test is paused, the device connection must be disconnected From 986477a3b74e79fdab66054ed5338fb4c1e7da25 Mon Sep 17 00:00:00 2001 From: Liam Gerrior <84335026+GerriorL@users.noreply.github.com> Date: Thu, 25 May 2023 10:54:48 -0400 Subject: [PATCH 22/61] Update docs/aetest/debugging.rst Co-authored-by: Thomas Ryan --- docs/aetest/debugging.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/aetest/debugging.rst b/docs/aetest/debugging.rst index 87360e8..001dfad 100644 --- a/docs/aetest/debugging.rst +++ b/docs/aetest/debugging.rst @@ -513,8 +513,10 @@ The pause will also dump the connection information, including which devices are # +-------------------+--------------------------------+ -If it is required to connect to the device directly while the test is paused, the device connection must be disconnected -from before it can be accessed directly. +.. note:: + + If it is required to connect to the device directly while the test is paused, the device connection must be disconnected + from before it can be accessed directly. .. code-block:: python From 4f3669ddc79aff204d25f2abc9985b32e938fa5d Mon Sep 17 00:00:00 2001 From: Liam Gerrior <84335026+GerriorL@users.noreply.github.com> Date: Thu, 25 May 2023 10:55:08 -0400 Subject: [PATCH 23/61] Update docs/aetest/debugging.rst Co-authored-by: bastell <31291038+bastell@users.noreply.github.com> --- docs/aetest/debugging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/aetest/debugging.rst b/docs/aetest/debugging.rst index 001dfad..93997a5 100644 --- a/docs/aetest/debugging.rst +++ b/docs/aetest/debugging.rst @@ -521,7 +521,7 @@ The pause will also dump the connection information, including which devices are .. code-block:: python >>> import pyats.easypy as ep - >>> dev = ep.runtime.testbed.devices.uut + >>> dev = ep.runtime.testbed.devices[''] >>> dev.disconnect() # the device can now be connected to in a separate terminal. Once ready to resume the test, From b2ada19946d8d99a626903376e6402fbd7072877 Mon Sep 17 00:00:00 2001 From: lgerrior Date: Tue, 30 May 2023 13:50:49 -0400 Subject: [PATCH 24/61] Update Changelogs --- docs/changelog/2023/may.rst | 70 +++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 71 insertions(+) create mode 100644 docs/changelog/2023/may.rst diff --git a/docs/changelog/2023/may.rst b/docs/changelog/2023/may.rst new file mode 100644 index 0000000..c8fc75b --- /dev/null +++ b/docs/changelog/2023/may.rst @@ -0,0 +1,70 @@ +May 2023 +========== + +May 30 - Pyats v23.5 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.5 + ``pyats.aereport``, v23.5 + ``pyats.aetest``, v23.5 + ``pyats.async``, v23.5 + ``pyats.cisco``, v23.5 + ``pyats.connections``, v23.5 + ``pyats.datastructures``, v23.5 + ``pyats.easypy``, v23.5 + ``pyats.kleenex``, v23.5 + ``pyats.log``, v23.5 + ``pyats.reporter``, v23.5 + ``pyats.results``, v23.5 + ``pyats.robot``, v23.5 + ``pyats.tcl``, v23.5 + ``pyats.topology``, v23.5 + ``pyats.utils``, v23.5 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.pyats + * Add a cli for manifest to use symlink + +* pyats.easypy + * Add checking for using symlink for the manifest + * Update manifest execution logic to allow jobfiles to be symlinked to another folder + +* aetest + * Modified PausePdb + * Dumps the connection information from the testbed to the console + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* easypy + * Added suite_id to all the jobs reports + * Add suite_name to report data from suite-name CLI argument + * Added label and component reporting options + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index efc56bf..65b6c19 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/may 2023/march 2023/february 2023/january From e8f59f035f2616fc9b99fee9247dbdb61af2ffae Mon Sep 17 00:00:00 2001 From: GerriorL <84335026+gerriorl@users.noreply.github.com> Date: Fri, 26 May 2023 11:39:55 -0400 Subject: [PATCH 25/61] Update index.rst --- docs/changelog/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 78da33f..25230c9 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,8 +8,8 @@ Changelog .. toctree:: :maxdepth: 2 - 2023/april 2023/may + 2023/april 2023/march 2023/february 2023/january From 0c7741e5a1995eb8c338a622de4842b8f48130eb Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Thu, 22 Jun 2023 19:50:27 -0400 Subject: [PATCH 26/61] Releasing v23.6 --- docs/changelog/2023/june.rst | 55 ++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 56 insertions(+) create mode 100644 docs/changelog/2023/june.rst diff --git a/docs/changelog/2023/june.rst b/docs/changelog/2023/june.rst new file mode 100644 index 0000000..043a324 --- /dev/null +++ b/docs/changelog/2023/june.rst @@ -0,0 +1,55 @@ +June 2023 +========== + +June 27 - Pyats v23.6 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.6 + ``pyats.aereport``, v23.6 + ``pyats.aetest``, v23.6 + ``pyats.async``, v23.6 + ``pyats.cisco``, v23.6 + ``pyats.connections``, v23.6 + ``pyats.datastructures``, v23.6 + ``pyats.easypy``, v23.6 + ``pyats.kleenex``, v23.6 + ``pyats.log``, v23.6 + ``pyats.reporter``, v23.6 + ``pyats.results``, v23.6 + ``pyats.robot``, v23.6 + ``pyats.tcl``, v23.6 + ``pyats.topology``, v23.6 + ``pyats.utils``, v23.6 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.easypy + * Fixed issue with multiprocessing causing socket usage collisions + +* pyats.kleenex + * Updated bringup logic to allow users to specify any interface type + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 25230c9..fdef660 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/june 2023/may 2023/april 2023/march From 52bc8a5a8b05e738dc197d56c46fffd174410f4c Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Tue, 25 Jul 2023 08:17:48 -0700 Subject: [PATCH 27/61] Update changelogs --- docs/changelog/2023/july.rst | 52 ++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 53 insertions(+) create mode 100644 docs/changelog/2023/july.rst diff --git a/docs/changelog/2023/july.rst b/docs/changelog/2023/july.rst new file mode 100644 index 0000000..ace8f31 --- /dev/null +++ b/docs/changelog/2023/july.rst @@ -0,0 +1,52 @@ +July 2023 +========== + +July 24 - Pyats v23.7 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.7 + ``pyats.aereport``, v23.7 + ``pyats.aetest``, v23.7 + ``pyats.async``, v23.7 + ``pyats.cisco``, v23.7 + ``pyats.connections``, v23.7 + ``pyats.datastructures``, v23.7 + ``pyats.easypy``, v23.7 + ``pyats.kleenex``, v23.7 + ``pyats.log``, v23.7 + ``pyats.reporter``, v23.7 + ``pyats.results``, v23.7 + ``pyats.robot``, v23.7 + ``pyats.tcl``, v23.7 + ``pyats.topology``, v23.7 + ``pyats.utils``, v23.7 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* ats.kleenex + * Allow users to override device `type` and `alias` with values from logical testbed + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index fdef660..2f4b044 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/july 2023/june 2023/may 2023/april From 1740a825cf4c16effbf4fcc0a6b5faf4a5038936 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Tue, 25 Jul 2023 09:34:35 -0700 Subject: [PATCH 28/61] Releasing v23.7 From a147a636f41afad4c3783b82c296478aa8f0ac81 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Wed, 26 Jul 2023 07:29:00 +1200 Subject: [PATCH 29/61] Docs for CALLABLE markup with kwarg --- docs/utilities/yaml_markup.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/utilities/yaml_markup.rst b/docs/utilities/yaml_markup.rst index 0858b72..d2178c5 100644 --- a/docs/utilities/yaml_markup.rst +++ b/docs/utilities/yaml_markup.rst @@ -29,6 +29,7 @@ Reference # %ENV{environment_variable_name} # %CALLABLE{path_to_callable} # %CALLABLE{path_to_callable(param1,param2,param3)} + # %CALLABLE{path_to_callable(param1='val1',param2='val2')} # %INCLUDE{yaml_file_path} # %ASK{optional prompt text} # %ENC{encoded text} @@ -101,6 +102,10 @@ Markup Examples # (replaced with actual path to callable) %CALLABLE{path.to.callable(param1,param2,param3)} + # reference to callable with paramter param1 and + # kwarg style parameter param2 + %CALLABLE{path.to.callable(param1, param2='val1')} + # reference to content from other YAML file # (replaced with actual path to YAML file) %INCLUDE{yaml_file_path} From c42d2b93acc07966653485255eb037f5e2caabf1 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Wed, 26 Jul 2023 12:05:02 -0700 Subject: [PATCH 30/61] Releasign v23.7 --- docs/changelog/2023/july.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog/2023/july.rst b/docs/changelog/2023/july.rst index ace8f31..9199c12 100644 --- a/docs/changelog/2023/july.rst +++ b/docs/changelog/2023/july.rst @@ -1,7 +1,7 @@ July 2023 ========== -July 24 - Pyats v23.7 +July 25 - Pyats v23.7 ------------------------ From a79148dcf7efd21b02b3cca734efc768a7b41fa7 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Tue, 18 Jul 2023 14:35:08 +1200 Subject: [PATCH 31/61] Add section stats docs --- docs/configuration/index.rst | 2 ++ docs/easypy/email.rst | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/configuration/index.rst b/docs/configuration/index.rst index 2d03605..ac46259 100644 --- a/docs/configuration/index.rst +++ b/docs/configuration/index.rst @@ -126,6 +126,8 @@ The following fields are currently open for user to customize. format = # Collect git info, default is True. git_info = + # section reporting, default is "SubSection SetupSection CleanupSection TestSection" + sections = SubSection SetupSection CleanupSection TestSection # configuration related to timestamps [timestamp] diff --git a/docs/easypy/email.rst b/docs/easypy/email.rst index 940dda0..049b49c 100644 --- a/docs/easypy/email.rst +++ b/docs/easypy/email.rst @@ -51,7 +51,20 @@ The following describes the default email report content. Note that fields in TOTAL : {runtime.job.results[total]} - Success Rate : {runtime.job.results[success_rate]:.02f} % + Success Rate : {runtime.job.results[success_rate]:.02f} + + Section Stats + Passed : {runtime.job.section_results[passed]} + Passx : {runtime.job.section_results[passx]} + Failed : {runtime.job.section_results[failed]} + Aborted : {runtime.job.section_results[aborted]} + Blocked : {runtime.job.section_results[blocked]} + Skipped : {runtime.job.section_results[skipped]} + Errored : {runtime.job.section_results[errored]} + + TOTAL : {runtime.job.section_results[total]} + + Section Success Rate : {runtime.job.section_results[success_rate]:.02f} +------------------------------------------------------------------------------+ | Task Result Summary | @@ -91,6 +104,24 @@ host, update the following fields in pyATS :ref:`pyats_configuration`'s Report Customization -------------------- +The email and job summary report includes Overall Stats which is based on the +count of CommonSetup, CommonCleanup and Testcase classes defined in test +scripts. The report also includes Section Stats that summarize SubSection, +SetupSection, CleanupSection and TestSection stats. + +The section reporting can be changed by updating the `report.sections` +configuration to define which sections should be counted for reporting purposes. + +For example, to include only TestSections (decorated with `@aetest.test`), Setup +(`aetest.setup`) and Cleanup (`@aetest.cleanup`) sections, set `report.sections` +as below. The values for the sections are space separated. + +.. code-block:: ini + + # report configuration + [report] + sections = TestSection SetupSection CleanupSection + Easypy email notification reports are fully customizable, allowing users to attach custom information to the email report body. From 2edc39e3cf4d64bf9810dfb6e137207b23bfbebe Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Wed, 26 Jul 2023 07:28:23 +1200 Subject: [PATCH 32/61] Update section stats doc --- docs/easypy/email.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/easypy/email.rst b/docs/easypy/email.rst index 049b49c..c13fd06 100644 --- a/docs/easypy/email.rst +++ b/docs/easypy/email.rst @@ -105,9 +105,9 @@ Report Customization -------------------- The email and job summary report includes Overall Stats which is based on the -count of CommonSetup, CommonCleanup and Testcase classes defined in test -scripts. The report also includes Section Stats that summarize SubSection, -SetupSection, CleanupSection and TestSection stats. +count of CommonSetup, CommonCleanup, Testcase, Trigger and Verification +classes defined in test scripts. The report also includes Section Stats that +summarize SubSection, SetupSection, CleanupSection and TestSection stats. The section reporting can be changed by updating the `report.sections` configuration to define which sections should be counted for reporting purposes. From dc71a1dfda3e14cd8bbfb6356c9ddf6f37ebc134 Mon Sep 17 00:00:00 2001 From: Dave Wapstra Date: Fri, 4 Aug 2023 10:40:46 +1200 Subject: [PATCH 33/61] Update email docs --- docs/easypy/email.rst | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/docs/easypy/email.rst b/docs/easypy/email.rst index c13fd06..6686f75 100644 --- a/docs/easypy/email.rst +++ b/docs/easypy/email.rst @@ -279,8 +279,42 @@ at any time prior to calling the send() method. - **to_email**: A list or tuple of recipient addresses. - **subject**: The subject line of the email. - **body**: The body text. This should be a plain text message. - - **attachments**: A list of attachments to put on the message. These can be - either email.MIMEBase.MIMEBase instances, or (filename, content, mimetype) - triples - currently only supports MIMEText. + - **attachments**: A list of attachments to put on the message. Currently only supports MIMEText. - **html_email**: flag to enable alternative HTML email format. - **html_body**: Body in HTML format. + +.. code-block:: python + + # Example + # ------- + # + # Sending email from pyATS + + # import EmailMsg class + from pyats.utils.email import EmailMsg + + # Create email message object + mail = EmailMsg( + from_email=email_from_address, + to_email=email_to_address, + subject=email_subject, + body=email_body) + + # send email + mail.send() + + # If you want to send HTML email: + mail = EmailMsg( + from_email=email_from_address, + to_email=email_to_address, + subject=email_subject, + html_email=True, + html_body=html_body) + + # You can also add attachments: + mail = EmailMsg( + from_email=email_from_address, + to_email=email_to_address, + subject=email_subject, + body=email_body, + attachments=['/path/to/file']) From d8660b499ab415e491619e2143a612d4326fd43e Mon Sep 17 00:00:00 2001 From: omid Date: Tue, 29 Aug 2023 09:18:36 -0400 Subject: [PATCH 34/61] Releasing v23.8 --- docs/changelog/2023/august.rst | 70 ++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 71 insertions(+) create mode 100644 docs/changelog/2023/august.rst diff --git a/docs/changelog/2023/august.rst b/docs/changelog/2023/august.rst new file mode 100644 index 0000000..933aefb --- /dev/null +++ b/docs/changelog/2023/august.rst @@ -0,0 +1,70 @@ +August 2023 +========== + +August 29 - Pyats v23.8 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.8 + ``pyats.aereport``, v23.8 + ``pyats.aetest``, v23.8 + ``pyats.async``, v23.8 + ``pyats.cisco``, v23.8 + ``pyats.connections``, v23.8 + ``pyats.datastructures``, v23.8 + ``pyats.easypy``, v23.8 + ``pyats.kleenex``, v23.8 + ``pyats.log``, v23.8 + ``pyats.reporter``, v23.8 + ``pyats.results``, v23.8 + ``pyats.robot``, v23.8 + ``pyats.tcl``, v23.8 + ``pyats.topology``, v23.8 + ``pyats.utils``, v23.8 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats.aetest + * Added support for YAML Markup with datafiles, including testbed references + +* pyats.utils + * add new function chainattrget + +* pyats.easypy + * Added section result statistics to job report + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.kleenex + * Update device name mapping logic to support missing entries from orchestrator + * Fix device names used with actual testbed vs logical testbed for use with clean image overrides + +* pyats.utils + * Updated implementation of enforce_max_key_value_length to support hierarchical dictionaries + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 2f4b044..e15aa60 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/august 2023/july 2023/june 2023/may From 3eff740d91512b985c6b9aecdb3183d5218572d2 Mon Sep 17 00:00:00 2001 From: tasarath Date: Fri, 22 Sep 2023 12:11:10 -0400 Subject: [PATCH 35/61] Releasing v23.9 --- docs/changelog/2023/september.rst | 87 +++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 88 insertions(+) create mode 100644 docs/changelog/2023/september.rst diff --git a/docs/changelog/2023/september.rst b/docs/changelog/2023/september.rst new file mode 100644 index 0000000..d14fb2b --- /dev/null +++ b/docs/changelog/2023/september.rst @@ -0,0 +1,87 @@ +September 2023 +========== + +September 26 - Pyats v23.9 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.9 + ``pyats.aereport``, v23.9 + ``pyats.aetest``, v23.9 + ``pyats.async``, v23.9 + ``pyats.cisco``, v23.9 + ``pyats.connections``, v23.9 + ``pyats.datastructures``, v23.9 + ``pyats.easypy``, v23.9 + ``pyats.kleenex``, v23.9 + ``pyats.log``, v23.9 + ``pyats.reporter``, v23.9 + ``pyats.results``, v23.9 + ``pyats.robot``, v23.9 + ``pyats.tcl``, v23.9 + ``pyats.topology``, v23.9 + ``pyats.utils``, v23.9 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats + * Added BaseServiceWrapper class + +* pyats.aetest + * Added support for YAML Markup with datafiles, including testbed references + +* pyats.utils + * add new function chainattrget + * Added duplicate key detection in yaml loader + +* pyats.easypy + * Added section result statistics to job report + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.kleenex + * Update device name mapping logic to support missing entries from orchestrator + * Fix device names used with actual testbed vs logical testbed for use with clean image overrides + +* pyats.topology + * Updated management schema type for protocols. + +* pyats.utils + * Updated implementation of enforce_max_key_value_length to support hierarchical dictionaries + +* kleenex + * Modified KleenexMarkupProcessor + * Delayed processing of testbed markups to support logical testbeds + * Modified kleenex_main + * Reloaded clean file to process testbed markups after topology bringup + +* easypy + * Modified Kleenex Plugin + * Reloaded clean file to process testbed markups after topology bringup + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index e15aa60..45277d6 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/september 2023/august 2023/july 2023/june From b54237651e9d8c89bfade0ae42d82b826fd0c550 Mon Sep 17 00:00:00 2001 From: omid Date: Mon, 16 Oct 2023 13:44:26 -0400 Subject: [PATCH 36/61] update schema --- docs/topology/schema.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/topology/schema.rst b/docs/topology/schema.rst index f531722..3331015 100644 --- a/docs/topology/schema.rst +++ b/docs/topology/schema.rst @@ -220,8 +220,8 @@ Production YAML Schema peripherals: # device hardware block # may contain anything describing peripherals - # connected to the device. - # currently supported devices are terminal servers + # connected to the device. + # currently supported devices are terminal servers # and power cyclers. # (optional) @@ -272,6 +272,8 @@ Production YAML Schema # to apis such as connect(), start_pool(), etc. # (default: None - let the connection class decide) # (optional) + fallback_credentials: # list of the fallback credentials to be used for + # the connections in the testbed.(optional) connections: # a list of subconnections for a multi-console # device. @@ -311,6 +313,9 @@ Production YAML Schema port: # port to connect to # (optional) + fallback_credentials: # list of the fallback credential for this + # connection.(optional) + credentials: # credential details specific to this connection # (optional) From 6ec71b8c488233f348d23eee5dd9e84f06ae3f92 Mon Sep 17 00:00:00 2001 From: omid Date: Mon, 16 Oct 2023 13:46:18 -0400 Subject: [PATCH 37/61] update the documentation --- docs/topology/schema.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topology/schema.rst b/docs/topology/schema.rst index 3331015..5c201e9 100644 --- a/docs/topology/schema.rst +++ b/docs/topology/schema.rst @@ -313,7 +313,7 @@ Production YAML Schema port: # port to connect to # (optional) - fallback_credentials: # list of the fallback credential for this + fallback_credentials: # list of the fallback credentials for this # connection.(optional) credentials: From 4888f049c3d2f170e0646a807cd71e6030c79bb0 Mon Sep 17 00:00:00 2001 From: omid Date: Wed, 18 Oct 2023 15:25:48 -0400 Subject: [PATCH 38/61] address comments --- docs/topology/schema.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/topology/schema.rst b/docs/topology/schema.rst index 5c201e9..ad3ef6f 100644 --- a/docs/topology/schema.rst +++ b/docs/topology/schema.rst @@ -272,6 +272,7 @@ Production YAML Schema # to apis such as connect(), start_pool(), etc. # (default: None - let the connection class decide) # (optional) + fallback_credentials: # list of the fallback credentials to be used for # the connections in the testbed.(optional) From bca90eeb24f1d7d032822ccfdaf3969d3ab75285 Mon Sep 17 00:00:00 2001 From: "Ben Astell (bastell)" Date: Wed, 1 Nov 2023 17:08:26 -0400 Subject: [PATCH 39/61] Unify changelogs --- docs/changelog/2023/october.rst | 65 +++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 66 insertions(+) create mode 100644 docs/changelog/2023/october.rst diff --git a/docs/changelog/2023/october.rst b/docs/changelog/2023/october.rst new file mode 100644 index 0000000..e4f64f3 --- /dev/null +++ b/docs/changelog/2023/october.rst @@ -0,0 +1,65 @@ +October 2023 +========== + +October 31 - Pyats v23.10 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.10 + ``pyats.aereport``, v23.10 + ``pyats.aetest``, v23.10 + ``pyats.async``, v23.10 + ``pyats.cisco``, v23.10 + ``pyats.connections``, v23.10 + ``pyats.datastructures``, v23.10 + ``pyats.easypy``, v23.10 + ``pyats.kleenex``, v23.10 + ``pyats.log``, v23.10 + ``pyats.reporter``, v23.10 + ``pyats.results``, v23.10 + ``pyats.robot``, v23.10 + ``pyats.tcl``, v23.10 + ``pyats.topology``, v23.10 + ``pyats.utils``, v23.10 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.kleenex + * Modified markup processor to use chainattrget utility function to allow object attribute and key lookup + +* easypy + * Modified update_job + * Modified update_job to format elapsedtime + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* topology + * Updated schema for fallback credentials + * Add fallback credentials under connection + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 45277d6..8830bbb 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/october 2023/september 2023/august 2023/july From 33478d2aabd82589dd249f2ee12a11cfdad2a13a Mon Sep 17 00:00:00 2001 From: domachad Date: Tue, 28 Nov 2023 09:17:10 -0500 Subject: [PATCH 40/61] Releasing 23.11 --- docs/changelog/2023/november.rst | 95 ++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 96 insertions(+) create mode 100644 docs/changelog/2023/november.rst diff --git a/docs/changelog/2023/november.rst b/docs/changelog/2023/november.rst new file mode 100644 index 0000000..3b585e4 --- /dev/null +++ b/docs/changelog/2023/november.rst @@ -0,0 +1,95 @@ +November 2023 +========== + +November 27 - Pyats v23.11 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v23.11 + ``pyats.aereport``, v23.11 + ``pyats.aetest``, v23.11 + ``pyats.async``, v23.11 + ``pyats.cisco``, v23.11 + ``pyats.connections``, v23.11 + ``pyats.datastructures``, v23.11 + ``pyats.easypy``, v23.11 + ``pyats.kleenex``, v23.11 + ``pyats.log``, v23.11 + ``pyats.reporter``, v23.11 + ``pyats.results``, v23.11 + ``pyats.robot``, v23.11 + ``pyats.tcl``, v23.11 + ``pyats.topology``, v23.11 + ``pyats.utils``, v23.11 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* bringup.bases + * Added authenticate method to BringUpWorkerBase + * Class method to handle authentication before bringup + +* bringup.manager + * Modified __enter__ to authenticate + * Authentication is done (if required) before start_server is called + +* reporter + * Modified Testsuite/Task report + * Testsuite and Task now have an overall result in the final report + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* clean + * Modified bases.py + * Set default loglevel to be inherited from pyats + +* kleenex + * Modified kleenex package structure + * Split kleenex into bringup, clean, kleenex + * Bringup contains the scripts to bringup logical devices + * Clean contains the scripts common to bringup and kleenex + * Kleenex contains the scripts to execute clean + * Fixed issue with kleenex loader where the `--clean-image-json` argument wouldn't allow for device aliases to be passed + * Added unittests to verify changes + +* easypy + * Modified kleenex plugin + * Updated references to kleenex package structure + +* pyats.topology + * Updated schema to support services for testbed servers. + +* manifest + * Fixed issue with `parse_cli_args` where the `meta` argument would get nested in lists if it was specified more than two times + +* pyats.connections.bases + * Add CLI arguments to service wrapper + * Updated docs + * Detached service wrapper from easypy runtime + * Added steps to service wrapper methods to allow for reporting + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 8830bbb..71ec973 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2023/november 2023/october 2023/september 2023/august From 27e2dea0e3007271590d0d235ad577ad82578154 Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Thu, 25 Jan 2024 11:32:56 -0500 Subject: [PATCH 41/61] Releasing v24.1 --- docs/changelog/2024/january.rst | 108 ++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 109 insertions(+) create mode 100644 docs/changelog/2024/january.rst diff --git a/docs/changelog/2024/january.rst b/docs/changelog/2024/january.rst new file mode 100644 index 0000000..e962d0c --- /dev/null +++ b/docs/changelog/2024/january.rst @@ -0,0 +1,108 @@ +January 2024 +========== + +30 - Pyats v24.1 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.1 + ``pyats.aereport``, v24.1 + ``pyats.aetest``, v24.1 + ``pyats.async``, v24.1 + ``pyats.cisco``, v24.1 + ``pyats.connections``, v24.1 + ``pyats.datastructures``, v24.1 + ``pyats.easypy``, v24.1 + ``pyats.kleenex``, v24.1 + ``pyats.log``, v24.1 + ``pyats.reporter``, v24.1 + ``pyats.results``, v24.1 + ``pyats.robot``, v24.1 + ``pyats.tcl``, v24.1 + ``pyats.topology``, v24.1 + ``pyats.utils``, v24.1 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* kleenex + * Fixed issue with kleenex loader handling device alias + +* pyats.configuration + * Removed pyATS Configuration object from sys.modules, refactored usage + * Change "from pyats import configuration" to "from pyats.configuration import configuration" + +* log-pkg + * commands + * remove aiohttp-swagger and its references to resolve markupsafe errors + +* pyats.utils + * Updated YAML markup processor to support markup in dictionary keys + +* easypy + * Removed + * Removed duplicate Task Result Details and Task Result Summary + +* pyats.topology + * Updated management schema type for address and gateway. + +* pyats.kleenex + * Modified p_reference_markup + * Parse credentials on testbed.raw_config + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* easypy.plugins + * kleenex.py + * Added preprocessor logic to update default clean image + +* cisco.commands + * Added pyats testbed teardown + * Added command to teardown topology using dyntopo orchestrators + * Added pyats testbed bringup + * Added command to bringup topology using dyntopo orchestrators + +* easypy + * AEReporter + * add skipped to AEPluginReporter + * Kleenex + * Add skip-teardown-on-failure argument to skip teardown when success rate is below 100% + +* pyats.utils + * Added load_dict_from_list + * Added support for meta argument if passed as a list + * Support keyword argument (kwarg) syntax with CALLABLE YAML markup + +* bringup.bases + * Added skip_teardown argument + * Added argument to skip topology teardown after clean/bringup execution. + +* easypy.plugins.kleenex + * Added skip_teardown argument + * Added argument to skip topology teardown after test case execution. + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 71ec973..abf900d 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/january 2023/november 2023/october 2023/september From 01233bfcb0b606c5b4c3ce05f5ddc63b2199cf11 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Sun, 25 Feb 2024 21:33:40 -0800 Subject: [PATCH 42/61] update changelogs --- docs/changelog/2024/february.rst | 101 +++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 102 insertions(+) create mode 100644 docs/changelog/2024/february.rst diff --git a/docs/changelog/2024/february.rst b/docs/changelog/2024/february.rst new file mode 100644 index 0000000..e0e610f --- /dev/null +++ b/docs/changelog/2024/february.rst @@ -0,0 +1,101 @@ +February 2024 +========== + +February 27 - Pyats v24.2 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.2 + ``pyats.aereport``, v24.2 + ``pyats.aetest``, v24.2 + ``pyats.async``, v24.2 + ``pyats.cisco``, v24.2 + ``pyats.connections``, v24.2 + ``pyats.datastructures``, v24.2 + ``pyats.easypy``, v24.2 + ``pyats.kleenex``, v24.2 + ``pyats.log``, v24.2 + ``pyats.reporter``, v24.2 + ``pyats.results``, v24.2 + ``pyats.robot``, v24.2 + ``pyats.tcl``, v24.2 + ``pyats.topology``, v24.2 + ``pyats.utils``, v24.2 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Add +-------------------------------------------------------------------------------- + +* docker + * Added aarch64 python 3.12 builder + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* makefile + * Added aiohttp package + +* easypy.plugins + * kleenex.py + * Added logic to update device recovery information to clean data. + * kleenex.py + * Added dyntopo entrypoints to take cli args from the workers. + + +-------------------------------------------------------------------------------- + Modify +-------------------------------------------------------------------------------- + +* aetest-pkg + * Removed unused and deprecated imports + * Renamed the file to fix UT issue + +* easypy-pkg + * assertEquals is not support for 3.12 hence changed to assertEqual + +* utils-pkg + * updated import utils with exec_module instead of load_module + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* installer + * Modified installer script to remove dependency on pypi.org (Cisco internal use only) + +* pyats.connections + * utils.py + * Fixed bug with set_parameters and added unittest + +* pyats.connections.bases + * Move wrapper to before connection so that `connect` can be wrapped + +* easypy.plugins + * Modified XunitPlugin + * Updated post_job method and separated xunit xml generation code + * Added conditional xunit_include_logs parameter to generate xunit.xml + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index abf900d..c0cd25e 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/february 2024/january 2023/november 2023/october From bafd8696885ccc7607e3043998c64389284562c9 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Sun, 25 Feb 2024 22:13:56 -0800 Subject: [PATCH 43/61] Releasing v24.2 From 45bee4ca74a47cbf7a78c0f636e8669cb8ac7a71 Mon Sep 17 00:00:00 2001 From: omid Date: Thu, 21 Mar 2024 12:53:17 -0400 Subject: [PATCH 44/61] Releasing v24.3 --- docs/changelog/2024/march.rst | 79 +++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 80 insertions(+) create mode 100644 docs/changelog/2024/march.rst diff --git a/docs/changelog/2024/march.rst b/docs/changelog/2024/march.rst new file mode 100644 index 0000000..a6da526 --- /dev/null +++ b/docs/changelog/2024/march.rst @@ -0,0 +1,79 @@ +March 2024 +========== + + - Pyats v24.3 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.3 + ``pyats.aereport``, v24.3 + ``pyats.aetest``, v24.3 + ``pyats.async``, v24.3 + ``pyats.cisco``, v24.3 + ``pyats.connections``, v24.3 + ``pyats.datastructures``, v24.3 + ``pyats.easypy``, v24.3 + ``pyats.kleenex``, v24.3 + ``pyats.log``, v24.3 + ``pyats.reporter``, v24.3 + ``pyats.results``, v24.3 + ``pyats.robot``, v24.3 + ``pyats.tcl``, v24.3 + ``pyats.topology``, v24.3 + ``pyats.utils``, v24.3 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* easypy.plugins + * rerun + * Added ability to download archive/rerun file from a URL + +* pyats.aetest + * Modified PauseOnPhrase, added collect and custom options + +* pyats.cli + * Migrate abstract + * Added functionality to the `pyats migrate abstract` command to modify the files in-place with the suggested changes + +* easypy + * plugins(kleenex) + * add --clean-template-action cli argument to kleenex plugin. + +* kleenex + * loader + * add logic for loading a template for cleaning the device + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.connection + * Service Wrapper + * Fixed an issue that caused a circular import when using the service wrapper with external connection packages + * Changed `BaseServiceWrapper` to `ServiceWrapper` + * Changed `ServiceWrapper` import from `from pyats.connections.bases import BaseServiceWrapper` to `from pyats.connections import ServiceWrapper` + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index c0cd25e..2704965 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/march 2024/february 2024/january 2023/november From aca0f1339999521924ea5bad7cf17ba039c8d888 Mon Sep 17 00:00:00 2001 From: omid Date: Tue, 26 Mar 2024 10:13:19 -0400 Subject: [PATCH 45/61] Releasing v24.3 From 946f206c07ad6d9f5b9095695e124c71eccd95de Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Mon, 15 Apr 2024 10:48:20 -0400 Subject: [PATCH 46/61] Added service wrapper docs --- docs/connections/hook.rst | 173 -------------------- docs/connections/index.rst | 2 +- docs/connections/wrapper.rst | 308 +++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+), 174 deletions(-) delete mode 100644 docs/connections/hook.rst create mode 100644 docs/connections/wrapper.rst diff --git a/docs/connections/hook.rst b/docs/connections/hook.rst deleted file mode 100644 index a151072..0000000 --- a/docs/connections/hook.rst +++ /dev/null @@ -1,173 +0,0 @@ -Connection Hook -=============== - -Whereas a connection class provides the fundamentals of communicating to target -devices using object methods (**services**), the goal of a connection hook is -to *tap* onto particular services and *inject* code to run before and after the -actual service call, **without modifying** the original behavior. - -Here are some typical use cases of connection hooks: - - - tracking the sequence/occurance of CLI command calls throughout a script - - - debug-logging the input arguments to, and output values from particular - services. - - - building a LRU cache based on script inputs and device states. - - - etc. - - -Usage ------ - -A connection hook wraps the original connection service method, and replaces it -during runtime (dynamically). The original functionality of the service remains -intact, but the hook enables uses to add code before/after the service method. - -- all connection hooks should inherit the base ``connections.hooks.ServiceHook`` - class - -- to add code *before a service*, define the ``enter()`` method under your - class. - - - all arguments to the service is passed to ``enter()`` method in the same way - as the original call. - -- to execute code *after a service*, define the ``after()`` method under your - class - - - the return of the service is passed to the ``after()`` method - -- to enable custom error/exception tracking, define a ``error_handle()`` method - under you class. - - - the current exception is passed into the error handler as ``e``. - -- to disable the current hook, call the ``restore()`` method of the hook. - -.. note:: - - error handlers can suppress an exception, and/or track/register it - internally. By default the built-in error handler will simply raise the - current exception. Developer can modify that to suppress the current - exception being handled, and return a fixed/altered result. - - -.. code-block:: python - - # Example - # ------- - # - # a simple tracking implementation - - import pdb - import collections - - from pyats.connections.hooks import ServiceHook - - class Tracker(ServiceHook): - '''A hook implementation intended to count the number of CLI calls, and - track their result returns''' - - def __init__(self, *args, **kwargs): - - # always call the parent - super().__init__(*args, **kwargs) - - # create a local counter - self.counter = collections.Counter() - - # create a command return storage dictionary - self.returns = {} - - def enter(self, cmd, *args, **kwargs): - '''enter - - Track the command occurance (calls) by assuming execute() command's - first argument is the CLI command to run, and ignoring the rest of - the arguments - ''' - - # increment the counter - # (using this command as key) - self.counter[cmd] += 1 - - # store the current command for use in exit() - self.cmd = cmd - - def exit(self, retval): - '''exit - - store the return from the command call in another dictionary - ''' - - # the current command from enter() - cmd = self.cmd - - # current command occurance - index = self.counter[cmd] - - # because a command can be called multiple times, store each - # possible command using a dictionary with their counter as index - self.returns.setdefault(cmd, {}) - - # now store the return - self.returns[cmd][index] = retval - - def error_handle(self, e): - '''error_handle - - This dummy handler will just print the current exception and go into - pdb - that could be very useful! - - Note - ---- - for demonstration purpose only. - - NEVER do this in production :) you will BLOCK sanity/regression - automated runs. - ''' - - print(e) - print('-' * 80) - - # go into pdb - pdb.set_trace() - - # re-raise the exception - # (default behavior) - raise - - - # now that we've defined a hook implementation - # let's hook an actual device. - # ----------------------------------------------------------- - - # assuming we have a testbed from somewhere - from pyats.topology import loader - testbed = loader.load('/path/to/testbed.yaml') - - # get the device and connect to it - device = testbed.devices['my-device'] - device.connect() - - # use our hook and hook onto the execute() service - hook = Tracker(device, 'execute') - # note that device.execute is actually device.connections['default'].execute - # as per connection manager integration with device objects. - # thus it's actually more accurate to hook onto the connection itself - # eg: - # hook = Tracker(device.connections['default'], 'execute') - - # from here onwards, all calls to device.execute() will be tracked - device.execute('show version') - device.execute('show ip interface brief') - - # the returned hook instance can be used to check the hook returns & etc - hook.counter - hook.returns - - # to disable the hook behavior, call the restore() api. - hook.restore() - # this will restore the original functionality and disable the hook diff --git a/docs/connections/index.rst b/docs/connections/index.rst index 4d63dd4..7ee5b31 100644 --- a/docs/connections/index.rst +++ b/docs/connections/index.rst @@ -11,5 +11,5 @@ integrates into the pyATS topology model. integration manager class - hook + wrapper sharing \ No newline at end of file diff --git a/docs/connections/wrapper.rst b/docs/connections/wrapper.rst new file mode 100644 index 0000000..87de7f8 --- /dev/null +++ b/docs/connections/wrapper.rst @@ -0,0 +1,308 @@ +.. _service_wrapper:: + +Service Wrapper +=============== + +The Service Wrapper is a class designed to wrap various methods of a Connection object with pre, post, and exception handlers, adding extra functionality to existing Connection methods. It allows users to extend and customize the behavior of specific Connection methods without modifying the original implementation. + +Here are some typical use cases of service wrappers: + + - tracking the sequence/occurance of CLI command calls throughout a script + + - debug-logging the input arguments to, and output values from particular + services. + + - building a LRU cache based on script inputs and device states. + + - etc. + + +Usage +----- + +To use the Service Wrapper, follow these steps: + +1) Create a child class derived from the `pyats.connections.ServiceWrapper` class. This child class will serve as the Service Wrapper for the desired Connection methods. + +2) Set the `conn_type` and `service_name` of the child class to the Connection type and service name that the Service Wrapper will be applied to. This will typically be `unicon.Connection` and the name of the service method, such as `execute` or `configure`. The `order` class variable can also be set to determine the order in which the Service Wrappers will be applied. + +3) Define the conditions under which the Service Wrapper should be applied to the service using the `applicable` method. + +4) (Optional) Implement the necessary methods in the child class to define pre, post, and exception handling behavior. These methods will be executed at different stages during the service call. + +5) (Optional) Use Testcase `Steps` to provide more robust reporting and leverage the pyATS reporter functionality. + +6) (Optional) Configure CLI parser arguments in the child class using the `configure_parser` method. This is used to enable easy integration with CLI interfaces. + +.. note:: + + error handlers can suppress an exception, and/or track/register it + internally. By default the built-in error handler will simply raise the + current exception. Developer can modify that to suppress the current + exception being handled, and return a fixed/altered result. + + +Service Wrapper Class +--------------------- + +The Service Wrapper base class provides several methods that can be overwritten by the user to customize the behavior of the service call: + +`call_service` +`````````````` + +This method is responsible for calling the wrapped service. By default, it calls the service and passes any arguments that were provided to it. It returns the output of the service call, which can be utilized in the `post_service` method. If an error occurs, it calls the `exception_service` method. This method can be completely overloaded to change the behavior of the service call. + +.. code-block:: python + + def call_service(self, *args, **kwargs) -> Any: + """Call the service three times""" + ret_list = [] + for _ in range(3): + try: + ret_list.append(self.service(*args, **kwargs)) + except Exception as e: + logger.exception(f"Exception occurred: {e}") + return ret_list + +`exception_service` +``````````````````` + +The `exception_service` method is called if an exception occurs during the service call inside the `call_service` method. It is an abstracted method that will only run if the child class implements it. It can return anything to be used in the `post_service` method. + +.. code-block:: python + + def exception_service(self, e: Exception, *args, **kwargs) -> Any: + logger.exception(f"Exception occurred: {e}") + return f'Exception occurred: {e}' + +`pre_service` +````````````` + +The `pre_service` method is called before the `call_service` method is executed. It is an abstracted method that will only run if the child class implements it. This method allows performing any necessary actions or setup before the actual service call. + +.. code-block:: python + + def pre_service(self, *args, **kwargs) -> None: + logger.info(f"Running pre_service on {self.service_name}") + logger.info(f"{self.service_name} args: {args} kwargs: {kwargs}") + +`post_service` +`````````````` + +The `post_service` method is called after the `call_service` method is executed. It is an abstracted method that will only run if the child class implements it. This method is used to handle any post-processing actions after the service call. It receives the output of the `call_service` method as an argument. + +.. code-block:: python + + def post_service(self, output: Any, *args, **kwargs) -> None: + logger.info(f"Running post_service on {self.service_name}") + logger.info(f"{self.service_name} output: {output}") + +`configure_parser` +`````````````````` + +The `configure_parser` method is called when the Service Wrapper is loaded. It is used to configure CLI parser arguments, similar to an easypy plugin. This is a classmethod that requires definition from the implementor. It takes in a `parser` argument, which is an instance of the `argparse.ArgumentParser` class. + +.. code-block:: python + + @classmethod + def configure_parser(cls, parser: argparse.ArgumentParser) -> None: + parser.add_argument( + '--service-wrapper-arg', + dest='service_wrapper_arg', + action='store_true', + help='Service Wrapper argument', + ) + + return parser + +`applicable` +```````````` + +The `applicable` class method is used to determine whether the Service Wrapper should be applied to the service. This is also a classmethod that requires definition from the implementor. It takes in a `connection` argument, which is an instance of the `pyats.connections.BaseConnection` class. It should return a boolean value: `True` if the Service Wrapper should be applied, and `False` otherwise. + +This is in addition to the default `conn_type` and `service_name` checks that are done. This allows for more fine-grained control over which services the Service Wrapper is applied to. For example, the Service Wrapper can be applied to a specific device type, or only when a certain argument is passed to the service. + +.. code-block:: python + + # Override applicable to check if the device is an IOSXE device + @classmethod + def applicable(cls, connection: BaseConnection, *args, **kwargs) -> bool: + """Ensure the device OS is iosxe and the service wrapper argument is passed""" + return connection.device.os == 'iosxe' + +Important Attributes +```````````````````` + +The Service Wrapper base class also provides several attributes that can be used in the Service Wrapper methods: + +- `self.service` + - The service method that the Service Wrapper is applied to + - Note that this should be exclusively used to call the wrapper services. You **cannot** call `self.execute` for example as this does not exist. +- `self.connection` + - The Connection object that the Service Wrapper is applied to + - Note that caution should be taken when calling wrapped attributes on the connection object. It's possible to create an infinite loop if the wrapped attribute is also wrapped by the Service Wrapper. +- `self.device` + - The device object that the Connection object is connected to +- `self.logger` + - The logger object that can be used to log messages specific to the Service Wrapper +- `self.testcase` + - The testcase object that can be used to access testcase data + - This is the same testcase object that is passed to easypy plugins and has access to testcase data through `self.testcase.parameters` +- `self.args` + - The arguments as defined in the `configure_parser` method + +Class Variables +``````````````` + +In addition to the methods, the Service Wrapper base class also provides several class variables that must be set to define which service the Service Wrapper will be applied to: + +- `conn_type` + - The Connection type that the Service Wrapper will be applied to. Options include + + .. code-block:: python + + # This is a catch-all for all Connection types + pyats.connections.BaseConnection + + # This is the standard unicon connection + unicon.Connection + + # Used for rest connections + rest.connector.Rest + + # Used for yang connections + yang.connector.Gnmi + yang.connector.Netconf + +- `service_name` + - The service the wrapper will be used on. This will be the name of the service method, such as `execute` or `configure` + +- `order` + - This is an optional variable. It's is used to determine the order in which the Service Wrappers will be applied. It is an integer value, with higher values being applied first. The default value is `0`. + +Examples +-------- + +Execute Service Wrapper - IOSXE Device +`````````````````````````````````````` + +This is an example of a service wrapper for wrapping the execute service on an IOSXE device + +.. note:: This example wrapper will run for ALL IOSXE devices when this wrapper script is installed. If this wrapper is installed in a shared environment, be aware that it affects all user jobs and any IOSXE devices in use. + +.. code-block:: python + + import unicon + from pyats.connections import ServiceWrapper + + class ExampleWrapper(ServiceWrapper): + conn_type = unicon.Connection + service_name = 'execute' + + @classmethod + def configure_parser(cls, parser) -> None: + parser.add_argument( + '--service-wrapper-arg', + dest='service_wrapper_arg', + action='store_true', + help='Service Wrapper argument', + ) + + return parser + + @classmethod + def applicable(cls, connection, *args, **kwargs) -> bool: + return connection.device.os == 'iosxe' + + def pre_service(self, *args, **kwargs) -> None: + self.logger.info(f"Running command: {args[0]} on {self.device.name}") + + def post_service(self, output, *args, **kwargs) -> None: + self.logger.info(f"Output: {output}") + + def call_service(self, *args, **kwargs) -> Any: + try: + self.logger.info(f"Calling service: {self.service_name}") + return self.service(*args, **kwargs) + except Exception as e: + return self.exception_service(e, *args, **kwargs) + + def exception_service(self, e, *args, **kwargs): + self.logger.exception(f"Exception occurred: {e}") + return f'Exception occurred: {e}' + +Steps +----- + +Inside of each service wrapper method you are able to pass a `steps` argument that will automatically pick up the Testcase's current `steps` object, which allows for use of the context manager style of reporting, failing, and passing a test. + +.. code-block:: python + + with steps.start() as step: + step.passed('Passed') + +This can be used in any of the four service wrapper methods. + +.. code-block:: python + + from pyats.connections import ServiceWrapper + + class ExampleWrapper(ServiceWrapper): + @classmethod + def applicable(cls, connection, *args, **kwargs) -> bool: + return True + + def pre_service(self, steps, *args, **kwargs) -> None: + with steps.start('Pre Service Step') as step: + step.passed('Sucessfully ran pre service step') + + def post_service(self, output, steps, *args, **kwargs) -> None: + with steps.start('Post Service Step') as step: + step.passed('Sucessfully ran post service step') + + def call_service(self, steps, *args, **kwargs) -> None: + with steps.start('Call Service Step') as step: + step.passed('Sucessfully ran call service step') + + def exception_service(self, e, steps, *args, **kwargs) -> None: + with steps.start('Exception Service Step') as step: + step.passed('Sucessfully ran exception service step') + +.. note:: The `steps` argument is only filled when the service wrapper is run in the context of a Testcase. If no Testcase is found, the `steps` argument will be `None`. Keep this in mind if your service wrapper is used in a standalone context with no Testcase. + +Execution +--------- + +Configuration Method +```````````````````` + +Once the service wrapper is created, you can utilize it by adding it to the +pyats configuration file. You can read up on how to configure that +:ref:`here. ` + +.. code-block:: ini + + [service_wrapper] + example_wrapper = path.to.module:ExampleWrapper + +Entrypoint Method +````````````````` + +Additionally, you can utilize it by adding it as an entry +point in your package's setup file through the `pyats.connections.wrapper` +entry point descriptor. This enables the service wrapper to be called and +employed within the context of your package, facilitating seamless integration +and utilization of the wrapped functionalities. + +.. code-block:: python + + setup( + ..., + + # console entry point + entry_points = { + 'pyats.connections.wrapper': [ + 'example_wrapper = path.to.path:ExampleWrapper' + ] + } + ) \ No newline at end of file From 232ad0c27d6b845be153e2a52a9d608658f36084 Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Tue, 16 Apr 2024 13:27:15 -0400 Subject: [PATCH 47/61] Addressed comments --- docs/changelog/2024/march.rst | 1 + docs/connections/wrapper.rst | 127 ++++++++++++++++++++++++++-------- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/docs/changelog/2024/march.rst b/docs/changelog/2024/march.rst index a6da526..bef8277 100644 --- a/docs/changelog/2024/march.rst +++ b/docs/changelog/2024/march.rst @@ -75,5 +75,6 @@ Changelogs * Fixed an issue that caused a circular import when using the service wrapper with external connection packages * Changed `BaseServiceWrapper` to `ServiceWrapper` * Changed `ServiceWrapper` import from `from pyats.connections.bases import BaseServiceWrapper` to `from pyats.connections import ServiceWrapper` + * This feature replaces the previous concept of Service Hooks diff --git a/docs/connections/wrapper.rst b/docs/connections/wrapper.rst index 87de7f8..f5a152c 100644 --- a/docs/connections/wrapper.rst +++ b/docs/connections/wrapper.rst @@ -3,7 +3,11 @@ Service Wrapper =============== -The Service Wrapper is a class designed to wrap various methods of a Connection object with pre, post, and exception handlers, adding extra functionality to existing Connection methods. It allows users to extend and customize the behavior of specific Connection methods without modifying the original implementation. +The Service Wrapper is a class designed to wrap various methods of a Connection +object with pre, post, and exception handlers, adding extra functionality to +existing Connection methods. It allows users to extend and customize the +behavior of specific Connection methods without modifying the original +implementation. Here are some typical use cases of service wrappers: @@ -22,21 +26,33 @@ Usage To use the Service Wrapper, follow these steps: -1) Create a child class derived from the `pyats.connections.ServiceWrapper` class. This child class will serve as the Service Wrapper for the desired Connection methods. +1) Create a child class derived from the `pyats.connections.ServiceWrapper` +class. This child class will serve as the Service Wrapper for the desired +Connection methods. -2) Set the `conn_type` and `service_name` of the child class to the Connection type and service name that the Service Wrapper will be applied to. This will typically be `unicon.Connection` and the name of the service method, such as `execute` or `configure`. The `order` class variable can also be set to determine the order in which the Service Wrappers will be applied. +2) Set the `conn_type` and `service_name` of the child class to the Connection +type and service name that the Service Wrapper will be applied to. This will +typically be `unicon.Connection` and the name of the service method, such as +`execute` or `configure`. The `order` class variable can also be set to +determine the order in which the Service Wrappers will be applied. -3) Define the conditions under which the Service Wrapper should be applied to the service using the `applicable` method. +3) Define the conditions under which the Service Wrapper should be applied to +the service using the `applicable` method. -4) (Optional) Implement the necessary methods in the child class to define pre, post, and exception handling behavior. These methods will be executed at different stages during the service call. +4) (Optional) Implement the necessary methods in the child class to define pre, +post, and exception handling behavior. These methods will be executed at +different stages during the service call. -5) (Optional) Use Testcase `Steps` to provide more robust reporting and leverage the pyATS reporter functionality. +5) (Optional) Use Testcase `Steps` to provide more robust reporting and +leverage the pyATS reporter functionality. -6) (Optional) Configure CLI parser arguments in the child class using the `configure_parser` method. This is used to enable easy integration with CLI interfaces. +6) (Optional) Configure CLI parser arguments in the child class using the +`configure_parser` method. This is used to enable easy integration with CLI +interfaces. .. note:: - error handlers can suppress an exception, and/or track/register it + Error handlers can suppress an exception, and/or track/register it internally. By default the built-in error handler will simply raise the current exception. Developer can modify that to suppress the current exception being handled, and return a fixed/altered result. @@ -45,12 +61,17 @@ To use the Service Wrapper, follow these steps: Service Wrapper Class --------------------- -The Service Wrapper base class provides several methods that can be overwritten by the user to customize the behavior of the service call: +The Service Wrapper base class provides several methods that can be overwritten +by the user to customize the behavior of the service call: `call_service` `````````````` -This method is responsible for calling the wrapped service. By default, it calls the service and passes any arguments that were provided to it. It returns the output of the service call, which can be utilized in the `post_service` method. If an error occurs, it calls the `exception_service` method. This method can be completely overloaded to change the behavior of the service call. +This method is responsible for calling the wrapped service. By default, it calls + the service and passes any arguments that were provided to it. It returns the + output of the service call, which can be utilized in the `post_service` method. + If an error occurs, it calls the `exception_service` method. This method can + be completely overloaded to change the behavior of the service call. .. code-block:: python @@ -67,7 +88,10 @@ This method is responsible for calling the wrapped service. By default, it calls `exception_service` ``````````````````` -The `exception_service` method is called if an exception occurs during the service call inside the `call_service` method. It is an abstracted method that will only run if the child class implements it. It can return anything to be used in the `post_service` method. +The `exception_service` method is called if an exception occurs during the +service call inside the `call_service` method. It is an abstracted method that +will only run if the child class implements it. It can return anything to be +used in the `post_service` method. .. code-block:: python @@ -78,7 +102,10 @@ The `exception_service` method is called if an exception occurs during the servi `pre_service` ````````````` -The `pre_service` method is called before the `call_service` method is executed. It is an abstracted method that will only run if the child class implements it. This method allows performing any necessary actions or setup before the actual service call. +The `pre_service` method is called before the `call_service` method is executed. + It is an abstracted method that will only run if the child class implements it. + This method allows performing any necessary actions or setup before the actual + service call. .. code-block:: python @@ -89,7 +116,10 @@ The `pre_service` method is called before the `call_service` method is executed. `post_service` `````````````` -The `post_service` method is called after the `call_service` method is executed. It is an abstracted method that will only run if the child class implements it. This method is used to handle any post-processing actions after the service call. It receives the output of the `call_service` method as an argument. +The `post_service` method is called after the `call_service` method is executed. + It is an abstracted method that will only run if the child class implements it. + This method is used to handle any post-processing actions after the service + call. It receives the output of the `call_service` method as an argument. .. code-block:: python @@ -100,7 +130,10 @@ The `post_service` method is called after the `call_service` method is executed. `configure_parser` `````````````````` -The `configure_parser` method is called when the Service Wrapper is loaded. It is used to configure CLI parser arguments, similar to an easypy plugin. This is a classmethod that requires definition from the implementor. It takes in a `parser` argument, which is an instance of the `argparse.ArgumentParser` class. +The `configure_parser` method is called when the Service Wrapper is loaded. It +is used to configure CLI parser arguments, similar to an easypy plugin. This is +a classmethod that requires definition from the implementor. It takes in a +`parser` argument, which is an instance of the `argparse.ArgumentParser` class. .. code-block:: python @@ -118,9 +151,18 @@ The `configure_parser` method is called when the Service Wrapper is loaded. It i `applicable` ```````````` -The `applicable` class method is used to determine whether the Service Wrapper should be applied to the service. This is also a classmethod that requires definition from the implementor. It takes in a `connection` argument, which is an instance of the `pyats.connections.BaseConnection` class. It should return a boolean value: `True` if the Service Wrapper should be applied, and `False` otherwise. +The `applicable` class method is used to determine whether the Service Wrapper +should be applied to the service. This is also a classmethod that requires +definition from the implementor. It takes in a `connection` argument, which is +an instance of the `pyats.connections.BaseConnection` class. It should return a +boolean value: `True` if the Service Wrapper should be applied, and `False` +otherwise. -This is in addition to the default `conn_type` and `service_name` checks that are done. This allows for more fine-grained control over which services the Service Wrapper is applied to. For example, the Service Wrapper can be applied to a specific device type, or only when a certain argument is passed to the service. +This is in addition to the default `conn_type` and `service_name` checks that +are done. This allows for more fine-grained control over which services the +Service Wrapper is applied to. For example, the Service Wrapper can be applied +to a specific device type, or only when a certain argument is passed to the +service. .. code-block:: python @@ -133,31 +175,40 @@ This is in addition to the default `conn_type` and `service_name` checks that ar Important Attributes ```````````````````` -The Service Wrapper base class also provides several attributes that can be used in the Service Wrapper methods: +The Service Wrapper base class also provides several attributes that can be used + in the Service Wrapper methods: - `self.service` - The service method that the Service Wrapper is applied to - - Note that this should be exclusively used to call the wrapper services. You **cannot** call `self.execute` for example as this does not exist. + - Note that this should be exclusively used to call the wrapper services. + You **cannot** call `self.execute` for example as this does not exist. - `self.connection` - The Connection object that the Service Wrapper is applied to - - Note that caution should be taken when calling wrapped attributes on the connection object. It's possible to create an infinite loop if the wrapped attribute is also wrapped by the Service Wrapper. + - Note that caution should be taken when calling wrapped attributes on the + connection object. It's possible to create an infinite loop if the wrapped + attribute is also wrapped by the Service Wrapper. - `self.device` - The device object that the Connection object is connected to - `self.logger` - - The logger object that can be used to log messages specific to the Service Wrapper + - The logger object that can be used to log messages specific to the Service + Wrapper - `self.testcase` - The testcase object that can be used to access testcase data - - This is the same testcase object that is passed to easypy plugins and has access to testcase data through `self.testcase.parameters` + - This is the same testcase object that is passed to easypy plugins and has + access to testcase data through `self.testcase.parameters` - `self.args` - The arguments as defined in the `configure_parser` method Class Variables ``````````````` -In addition to the methods, the Service Wrapper base class also provides several class variables that must be set to define which service the Service Wrapper will be applied to: +In addition to the methods, the Service Wrapper base class also provides several + class variables that must be set to define which service the Service Wrapper + will be applied to: - `conn_type` - - The Connection type that the Service Wrapper will be applied to. Options include + - The Connection type that the Service Wrapper will be applied to. Options + include .. code-block:: python @@ -175,10 +226,13 @@ In addition to the methods, the Service Wrapper base class also provides several yang.connector.Netconf - `service_name` - - The service the wrapper will be used on. This will be the name of the service method, such as `execute` or `configure` + - The service the wrapper will be used on. This will be the name of the + service method, such as `execute` or `configure` - `order` - - This is an optional variable. It's is used to determine the order in which the Service Wrappers will be applied. It is an integer value, with higher values being applied first. The default value is `0`. + - This is an optional variable. It's is used to determine the order in which + the Service Wrappers will be applied. It is an integer value, with higher + values being applied first. The default value is `0`. Examples -------- @@ -186,9 +240,12 @@ Examples Execute Service Wrapper - IOSXE Device `````````````````````````````````````` -This is an example of a service wrapper for wrapping the execute service on an IOSXE device +This is an example of a service wrapper for wrapping the execute service on an +IOSXE device -.. note:: This example wrapper will run for ALL IOSXE devices when this wrapper script is installed. If this wrapper is installed in a shared environment, be aware that it affects all user jobs and any IOSXE devices in use. +.. note:: This example wrapper will run for ALL IOSXE devices when this wrapper + script is installed. If this wrapper is installed in a shared environment, + be aware that it affects all user jobs and any IOSXE devices in use. .. code-block:: python @@ -234,7 +291,10 @@ This is an example of a service wrapper for wrapping the execute service on an I Steps ----- -Inside of each service wrapper method you are able to pass a `steps` argument that will automatically pick up the Testcase's current `steps` object, which allows for use of the context manager style of reporting, failing, and passing a test. +Inside of each service wrapper method you are able to pass a `steps` argument +that will automatically pick up the Testcase's current `steps` object, which +allows for use of the context manager style of reporting, failing, and passing +a test. .. code-block:: python @@ -268,11 +328,18 @@ This can be used in any of the four service wrapper methods. with steps.start('Exception Service Step') as step: step.passed('Sucessfully ran exception service step') -.. note:: The `steps` argument is only filled when the service wrapper is run in the context of a Testcase. If no Testcase is found, the `steps` argument will be `None`. Keep this in mind if your service wrapper is used in a standalone context with no Testcase. +.. note:: The `steps` argument is only filled when the service wrapper is run in + the context of a Testcase. If no Testcase is found, the `steps` argument + will be `None`. Keep this in mind if your service wrapper is used in a + standalone context with no Testcase. -Execution +Discovery --------- +There are two methods to enable pyATS to discover service wrappers. The first +method is to configure the service wrapper in the pyats configuration file. The +second method is to use an entry point. + Configuration Method ```````````````````` From 648f9b9c7631371dc95a4fe7a93ed15aa4e12a15 Mon Sep 17 00:00:00 2001 From: Taarini Sarath Chander Date: Tue, 30 Apr 2024 16:47:38 -0400 Subject: [PATCH 48/61] Releasing v24.4 --- docs/changelog/2024/april.rst | 102 ++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 103 insertions(+) create mode 100644 docs/changelog/2024/april.rst diff --git a/docs/changelog/2024/april.rst b/docs/changelog/2024/april.rst new file mode 100644 index 0000000..4c8f174 --- /dev/null +++ b/docs/changelog/2024/april.rst @@ -0,0 +1,102 @@ +April 2024 +========== + + - Pyats v24.4 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.4 + ``pyats.aereport``, v24.4 + ``pyats.aetest``, v24.4 + ``pyats.async``, v24.4 + ``pyats.cisco``, v24.4 + ``pyats.connections``, v24.4 + ``pyats.datastructures``, v24.4 + ``pyats.easypy``, v24.4 + ``pyats.kleenex``, v24.4 + ``pyats.log``, v24.4 + ``pyats.reporter``, v24.4 + ``pyats.results``, v24.4 + ``pyats.robot``, v24.4 + ``pyats.tcl``, v24.4 + ``pyats.topology``, v24.4 + ``pyats.utils``, v24.4 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats.easypy + * Added Tips Plugin + * A new plugin that provides a way to display tips and tricks to users + +* pyats.topology + * Updated Device class `repr`, added alias value + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* easypy + * Fix device recovery data type for tftp address + * Modified Plugin Reporting + * Plugins now report errors as results even when the plugin is not specified to report anything. This prevents the overall result showing Passed when a plugin had an error. + +* iosxe + * Added RerunPlugin + * Remove commonSetup, commonCleanup, testcases if it is none. + +* easypy/plugins + * kleenex + * updated logic to add device recovery section even if there + +* pyats.connections + * Modified connection manager + * Updated logic to support service wrapper with connection pools + * Pass Steps object to service wrapper if none found + +* pyats.aetest + * Modified Steps class to support index offset + +* pyats.async + * Support Steps with pcall + * Modified child process logic to update steps offset for child processes + * Add logic to propagate step failures in subprocess + +* bringup + * Manager + * Fixed bug preventing manager from halting exit with pyats clean command + +* kleenex + * loader + * Update to load and check the logical testbed in _preprocess_add_cli_images in order to + +* pyats.cisco + * Fix telemetry upload + +* utils/fileutils + * http + * Added logic for http stat in fileutils. + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 2704965..daa79ae 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/april 2024/march 2024/february 2024/january From 0d584c9a5328c512c1327de56f813954aec38a5e Mon Sep 17 00:00:00 2001 From: Taarini Sarath Chander Date: Tue, 30 Apr 2024 18:08:36 -0400 Subject: [PATCH 49/61] Updated docs --- docs/changelog/2024/april.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changelog/2024/april.rst b/docs/changelog/2024/april.rst index 4c8f174..f877e62 100644 --- a/docs/changelog/2024/april.rst +++ b/docs/changelog/2024/april.rst @@ -1,8 +1,8 @@ April 2024 ========== - - Pyats v24.4 ------------------------- +April 30 - Pyats v24.4 +---------------------- @@ -69,7 +69,7 @@ Changelogs * easypy/plugins * kleenex - * updated logic to add device recovery section even if there + * updated logic to add device recovery section even if there are no clean image attributes. * pyats.connections * Modified connection manager @@ -90,7 +90,7 @@ Changelogs * kleenex * loader - * Update to load and check the logical testbed in _preprocess_add_cli_images in order to + * Update to load and check the logical testbed in _preprocess_add_cli_images in order to get correct image_set for iol devices. * pyats.cisco * Fix telemetry upload From 7112826c7724a82ea98ec03ec2c5c8348e686237 Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Mon, 27 May 2024 11:14:52 -0400 Subject: [PATCH 50/61] Releasing v24.5 --- docs/changelog/2024/may.rst | 77 +++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 78 insertions(+) create mode 100644 docs/changelog/2024/may.rst diff --git a/docs/changelog/2024/may.rst b/docs/changelog/2024/may.rst new file mode 100644 index 0000000..addb779 --- /dev/null +++ b/docs/changelog/2024/may.rst @@ -0,0 +1,77 @@ +May 2024 +========== + +May 28 - Pyats v24.5 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.5 + ``pyats.aereport``, v24.5 + ``pyats.aetest``, v24.5 + ``pyats.async``, v24.5 + ``pyats.cisco``, v24.5 + ``pyats.connections``, v24.5 + ``pyats.datastructures``, v24.5 + ``pyats.easypy``, v24.5 + ``pyats.kleenex``, v24.5 + ``pyats.log``, v24.5 + ``pyats.reporter``, v24.5 + ``pyats.results``, v24.5 + ``pyats.robot``, v24.5 + ``pyats.tcl``, v24.5 + ``pyats.topology``, v24.5 + ``pyats.utils``, v24.5 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.cisco + * Update package discovery error handling + * Update ABS URL, add environment variable lookup + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* kleenex/worker + * Updated logic to make device alias name as default in reporter. + +* pyats/topology + * schema + * Update topology schema for rommon information. + +* clean + * schema + * Updated clean schema with resources section with memory and cpu. + +* cisco/commands/testbed + * bringup + * Updated logic to pull the dyntopo worker arguments dynamically. + +* cisco/commands + * testbed/export + * Added export subcommand to export the testbed data using laasv2 + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index daa79ae..17df925 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/may 2024/april 2024/march 2024/february From fc20c4051ccd405a6bff22441e983023ba7a73af Mon Sep 17 00:00:00 2001 From: omid Date: Mon, 24 Jun 2024 14:44:44 -0400 Subject: [PATCH 51/61] Releasing v24.6 --- docs/changelog/2024/june.rst | 63 ++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 64 insertions(+) create mode 100644 docs/changelog/2024/june.rst diff --git a/docs/changelog/2024/june.rst b/docs/changelog/2024/june.rst new file mode 100644 index 0000000..277caf3 --- /dev/null +++ b/docs/changelog/2024/june.rst @@ -0,0 +1,63 @@ +June 2024 +========== + + - Pyats v24.6 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.6 + ``pyats.aereport``, v24.6 + ``pyats.aetest``, v24.6 + ``pyats.async``, v24.6 + ``pyats.cisco``, v24.6 + ``pyats.connections``, v24.6 + ``pyats.datastructures``, v24.6 + ``pyats.easypy``, v24.6 + ``pyats.kleenex``, v24.6 + ``pyats.log``, v24.6 + ``pyats.reporter``, v24.6 + ``pyats.results``, v24.6 + ``pyats.robot``, v24.6 + ``pyats.tcl``, v24.6 + ``pyats.topology``, v24.6 + ``pyats.utils``, v24.6 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* easy/plugins + * kleenex + * Fixed clean default image issue. + * kleenex + * Fixed clean default image where multiple images being passed to each device, instead + +* kleenex + * clean/loader + * Fixed a bug with clean templates, where it was not updating the clean image properly. + +* topology + * Modified schema + * Added submodel key in device schema + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 17df925..a40e8ad 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/june 2024/may 2024/april 2024/march From 666dab6af8f880e9b1334b36638726d81c6bdffd Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Thu, 11 Jul 2024 11:27:16 -0700 Subject: [PATCH 52/61] fixed make docs warnings --- docs/apidoc/kleenex/index.rst | 1 + docs/apidoc/log/index.rst | 1 + docs/changelog/2020/index.rst | 1 + docs/index.rst | 1 + 4 files changed, 4 insertions(+) diff --git a/docs/apidoc/kleenex/index.rst b/docs/apidoc/kleenex/index.rst index e18e20f..753ffc5 100644 --- a/docs/apidoc/kleenex/index.rst +++ b/docs/apidoc/kleenex/index.rst @@ -13,6 +13,7 @@ Subpackages pyats.kleenex.loader pyats.kleenex.reporter + pyats.bringup Submodules ---------- diff --git a/docs/apidoc/log/index.rst b/docs/apidoc/log/index.rst index 1d1414e..a9723df 100644 --- a/docs/apidoc/log/index.rst +++ b/docs/apidoc/log/index.rst @@ -12,6 +12,7 @@ Subpackages .. toctree:: pyats.log.commands + pyats.atslog Submodules ---------- diff --git a/docs/changelog/2020/index.rst b/docs/changelog/2020/index.rst index 0eac7d5..3695da5 100644 --- a/docs/changelog/2020/index.rst +++ b/docs/changelog/2020/index.rst @@ -14,4 +14,5 @@ may apr mar + feb jan \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 543a8e6..43569cc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,6 +23,7 @@ studies and more. :maxdepth: 1 :caption: Main Components + aereport/index aetest/index easypy/index topology/index From d25ff397d169e7fd7831609ce30def476068c113 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Fri, 12 Jul 2024 10:44:43 -0700 Subject: [PATCH 53/61] cleanup docs --- docs/apidoc/easypy/pyats.easypy.runinfo.rst | 10 ---- docs/apidoc/index.rst | 1 - docs/apidoc/kleenex/index.rst | 18 +++--- ...enex.loader.rst => pyats.clean.loader.rst} | 6 +- .../apidoc/kleenex/pyats.kleenex.reporter.rst | 14 +---- docs/apidoc/log/pyats.log.commands.parser.rst | 6 +- docs/apidoc/tcl/index.rst | 59 ------------------- docs/changelog/2020/index.rst | 1 - 8 files changed, 16 insertions(+), 99 deletions(-) rename docs/apidoc/kleenex/{pyats.kleenex.loader.rst => pyats.clean.loader.rst} (59%) delete mode 100644 docs/apidoc/tcl/index.rst diff --git a/docs/apidoc/easypy/pyats.easypy.runinfo.rst b/docs/apidoc/easypy/pyats.easypy.runinfo.rst index e75abb5..d7dda52 100644 --- a/docs/apidoc/easypy/pyats.easypy.runinfo.rst +++ b/docs/apidoc/easypy/pyats.easypy.runinfo.rst @@ -22,14 +22,4 @@ Submodules :show-inheritance: -.. automodule:: pyats.easypy.runinfo.kafka - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.easypy.runinfo.s3 - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/apidoc/index.rst b/docs/apidoc/index.rst index 33145ac..8522a8a 100644 --- a/docs/apidoc/index.rst +++ b/docs/apidoc/index.rst @@ -13,7 +13,6 @@ API Documentation robot/index async/index datastructures/index - tcl/index log/index reporter/index results/index diff --git a/docs/apidoc/kleenex/index.rst b/docs/apidoc/kleenex/index.rst index 753ffc5..c0e63fe 100644 --- a/docs/apidoc/kleenex/index.rst +++ b/docs/apidoc/kleenex/index.rst @@ -11,7 +11,7 @@ Subpackages .. toctree:: - pyats.kleenex.loader + pyats.clean.loader pyats.kleenex.reporter pyats.bringup @@ -19,31 +19,31 @@ Submodules ---------- -.. automodule:: pyats.kleenex.bases +.. automodule:: pyats.bringup.bases :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.bringup_base_cli_parser +.. automodule:: pyats.bringup.base_cli_parser :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.bringup_manager +.. automodule:: pyats.bringup.manager :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.bringup_manager_cli_parser +.. automodule:: pyats.bringup.manager_cli_parser :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.bringup_signals +.. automodule:: pyats.bringup.signals :members: :undoc-members: :show-inheritance: @@ -67,19 +67,19 @@ Submodules :show-inheritance: -.. automodule:: pyats.kleenex.kleenex_traceback +.. automodule:: pyats.clean.traceback :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.schema +.. automodule:: pyats.clean.schema :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.kleenex.utils +.. automodule:: pyats.clean.utils :members: :undoc-members: :show-inheritance: diff --git a/docs/apidoc/kleenex/pyats.kleenex.loader.rst b/docs/apidoc/kleenex/pyats.clean.loader.rst similarity index 59% rename from docs/apidoc/kleenex/pyats.kleenex.loader.rst rename to docs/apidoc/kleenex/pyats.clean.loader.rst index d849f47..1a42c06 100644 --- a/docs/apidoc/kleenex/pyats.kleenex.loader.rst +++ b/docs/apidoc/kleenex/pyats.clean.loader.rst @@ -1,7 +1,7 @@ -pyats.kleenex.loader package +pyats.clean.loader package ============================ -.. automodule:: pyats.kleenex.loader +.. automodule:: pyats.clean.loader :members: :undoc-members: :show-inheritance: @@ -10,7 +10,7 @@ Submodules ---------- -.. automodule:: pyats.kleenex.loader.markup +.. automodule:: pyats.clean.loader.markup :members: :undoc-members: :show-inheritance: diff --git a/docs/apidoc/kleenex/pyats.kleenex.reporter.rst b/docs/apidoc/kleenex/pyats.kleenex.reporter.rst index e05f0cf..56a0c52 100644 --- a/docs/apidoc/kleenex/pyats.kleenex.reporter.rst +++ b/docs/apidoc/kleenex/pyats.kleenex.reporter.rst @@ -10,19 +10,7 @@ Submodules ---------- -.. automodule:: pyats.kleenex.reporter.base - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.kleenex.reporter.clean - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.kleenex.reporter.context +.. automodule:: pyats.kleenex.reporter.reporter :members: :undoc-members: :show-inheritance: diff --git a/docs/apidoc/log/pyats.log.commands.parser.rst b/docs/apidoc/log/pyats.log.commands.parser.rst index 45a28f0..33cb2ea 100644 --- a/docs/apidoc/log/pyats.log.commands.parser.rst +++ b/docs/apidoc/log/pyats.log.commands.parser.rst @@ -10,19 +10,19 @@ Submodules ---------- -.. automodule:: pyats.log.commands.parser.base +.. automodule:: pyats.log.commands.parser.base_parser :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.log.commands.parser.xml +.. automodule:: pyats.log.commands.parser.xml_parser :members: :undoc-members: :show-inheritance: -.. automodule:: pyats.log.commands.parser.yaml +.. automodule:: pyats.log.commands.parser.yaml_parser :members: :undoc-members: :show-inheritance: diff --git a/docs/apidoc/tcl/index.rst b/docs/apidoc/tcl/index.rst deleted file mode 100644 index fb8ff7b..0000000 --- a/docs/apidoc/tcl/index.rst +++ /dev/null @@ -1,59 +0,0 @@ -pyats.tcl package -================= - -.. automodule:: pyats.tcl - :members: - :undoc-members: - :show-inheritance: - -Submodules ----------- - - -.. automodule:: pyats.tcl.array - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.history - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.internal - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.interpreter - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.keyedlist - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.namespace - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.q - :members: - :undoc-members: - :show-inheritance: - - -.. automodule:: pyats.tcl.tclstr - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/changelog/2020/index.rst b/docs/changelog/2020/index.rst index 3695da5..20aa920 100644 --- a/docs/changelog/2020/index.rst +++ b/docs/changelog/2020/index.rst @@ -10,7 +10,6 @@ sep aug jul - jun may apr mar From d987b37303c730186058ae4783cc746a2a708139 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Tue, 30 Jul 2024 11:51:40 -0700 Subject: [PATCH 54/61] Release_24.7 --- docs/changelog/2024/july.rst | 69 ++++++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 70 insertions(+) create mode 100644 docs/changelog/2024/july.rst diff --git a/docs/changelog/2024/july.rst b/docs/changelog/2024/july.rst new file mode 100644 index 0000000..1b207e7 --- /dev/null +++ b/docs/changelog/2024/july.rst @@ -0,0 +1,69 @@ +July 2024 +========== + +July 30 - Pyats v24.7 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.7 + ``pyats.aereport``, v24.7 + ``pyats.aetest``, v24.7 + ``pyats.async``, v24.7 + ``pyats.cisco``, v24.7 + ``pyats.connections``, v24.7 + ``pyats.datastructures``, v24.7 + ``pyats.easypy``, v24.7 + ``pyats.kleenex``, v24.7 + ``pyats.log``, v24.7 + ``pyats.reporter``, v24.7 + ``pyats.results``, v24.7 + ``pyats.robot``, v24.7 + ``pyats.tcl``, v24.7 + ``pyats.topology``, v24.7 + ``pyats.utils``, v24.7 + +Upgrade Instructions +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + # DevNet Community + bash$ pip install --upgrade pyats + + # Cisco Internal Developers + bash$ pip install --upgrade ats + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* pyats.utils + * Modified argv parser helper function + * Ignore arguments after `--` + * Allow `-` to be passed as a value + +* pyats.cli + * Modified version update + * Ignore packages that have no matching version + + +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats.reporter + * update the log offset for each sub process + +* pyats + * remove init file for declaring namespaces for all the packages to avoid deprecation messages. + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index a40e8ad..56b86d8 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/july 2024/june 2024/may 2024/april From 2e5d8ae3d7f0c3ab492196225490072950f9636e Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Tue, 30 Jul 2024 12:47:33 -0700 Subject: [PATCH 55/61] Releasing v24.7 From a2bc70c641d3a51449d814534fe4a427253f04c2 Mon Sep 17 00:00:00 2001 From: Lukeman Hakkim Sheik Alavudeen Date: Wed, 31 Jul 2024 10:31:38 -0700 Subject: [PATCH 56/61] Releasing v24.7 From 5f39515d5103391653361dbddff6b5fdfaac552a Mon Sep 17 00:00:00 2001 From: Taarini Sarath Chander Date: Tue, 24 Sep 2024 20:58:26 -0400 Subject: [PATCH 57/61] Releasing v24.9 --- docs/changelog/2024/September.rst | 50 +++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 51 insertions(+) create mode 100644 docs/changelog/2024/September.rst diff --git a/docs/changelog/2024/September.rst b/docs/changelog/2024/September.rst new file mode 100644 index 0000000..b7d2101 --- /dev/null +++ b/docs/changelog/2024/September.rst @@ -0,0 +1,50 @@ +September 2024 +========== + +September 24 - Pyats v24.9 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.9 + ``pyats.aereport``, v24.9 + ``pyats.aetest``, v24.9 + ``pyats.async``, v24.9 + ``pyats.cisco``, v24.9 + ``pyats.connections``, v24.9 + ``pyats.datastructures``, v24.9 + ``pyats.easypy``, v24.9 + ``pyats.kleenex``, v24.9 + ``pyats.log``, v24.9 + ``pyats.reporter``, v24.9 + ``pyats.results``, v24.9 + ``pyats.robot``, v24.9 + ``pyats.tcl``, v24.9 + ``pyats.topology``, v24.9 + ``pyats.utils``, v24.9 + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* pyats.connections.wrapper + * Added add_service_wrapper_usage_data function to push service wrapper data to business telemetry. + + +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* topology + * schema + * Added device id to schema + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index 56b86d8..cb4ea07 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/September 2024/july 2024/june 2024/may From 26b7c1c702683959306dc5839b58f41660986cd7 Mon Sep 17 00:00:00 2001 From: Thomas Ryan Date: Tue, 22 Oct 2024 13:39:41 -0400 Subject: [PATCH 58/61] Releasing v24.10 --- docs/changelog/2024/october.rst | 41 +++++++++++++++++++++++++++++++++ docs/changelog/index.rst | 1 + 2 files changed, 42 insertions(+) create mode 100644 docs/changelog/2024/october.rst diff --git a/docs/changelog/2024/october.rst b/docs/changelog/2024/october.rst new file mode 100644 index 0000000..5eb7b1f --- /dev/null +++ b/docs/changelog/2024/october.rst @@ -0,0 +1,41 @@ +October 2024 +========== + +October 29 - Pyats v24.10 +------------------------ + + + +.. csv-table:: New Module Versions + :header: "Modules", "Version" + + ``pyats``, v24.10 + ``pyats.aereport``, v24.10 + ``pyats.aetest``, v24.10 + ``pyats.async``, v24.10 + ``pyats.cisco``, v24.10 + ``pyats.connections``, v24.10 + ``pyats.datastructures``, v24.10 + ``pyats.easypy``, v24.10 + ``pyats.kleenex``, v24.10 + ``pyats.log``, v24.10 + ``pyats.reporter``, v24.10 + ``pyats.results``, v24.10 + ``pyats.robot``, v24.10 + ``pyats.tcl``, v24.10 + ``pyats.topology``, v24.10 + ``pyats.utils``, v24.10 + + + + +Changelogs +^^^^^^^^^^ +-------------------------------------------------------------------------------- + Fix +-------------------------------------------------------------------------------- + +* fixed the schema for management routes + * Update to add static route ipv4 + + diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index cb4ea07..457a653 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -8,6 +8,7 @@ Changelog .. toctree:: :maxdepth: 2 + 2024/october 2024/September 2024/july 2024/june From e1a31ce9cb31fd311649db6e2e22877ad3bc3141 Mon Sep 17 00:00:00 2001 From: atestini Date: Fri, 22 Jul 2022 14:53:40 -0400 Subject: [PATCH 59/61] Improve readibility and wording --- docs/aetest/parameters.rst | 277 ++++++++++++++++++------------------- 1 file changed, 135 insertions(+), 142 deletions(-) diff --git a/docs/aetest/parameters.rst b/docs/aetest/parameters.rst index a5b93e8..96a52b8 100644 --- a/docs/aetest/parameters.rst +++ b/docs/aetest/parameters.rst @@ -7,27 +7,27 @@ Test Parameters - `Function Arguments`_ - `Data-Driven Programming`_ - - `Mutable/Immuntable Objects`_ + - `Mutable/Immutable Objects`_ - `Variable Scoping`_ .. _Data-Driven Programming: http://en.wikipedia.org/wiki/Data-driven_programming .. _Function Arguments: https://docs.python.org/3.4/tutorial/controlflow.html#more-on-defining-functions -.. _Mutable/Immuntable Objects: http://en.wikibooks.org/wiki/Python_Programming/Data_Types#Mutable_vs_Immutable_Objects +.. _Mutable/Immutable Objects: http://en.wikibooks.org/wiki/Python_Programming/Data_Types#Mutable_vs_Immutable_Objects .. _Variable Scoping: https://docs.python.org/3.4/reference/executionmodel.html -``aetest`` is a data-driven test infrastructure. Its scripts and testcases are +``aetest`` is a data-driven test infrastructure. Its scripts and test cases are intended to be driven dynamically by: -- data in the form of input arguments to the testscript -- dynamic data generated during runtime +- Data in the form of input arguments to the test script +- Dynamic data generated during runtime The collection of dynamic data that artificially affects the behavior of -testscripts and testcases in ``aetest`` is called **parameters**. It adheres to -a pre-defined set of parent/child propagation relationships, and may be used as +test scripts and test cases in ``aetest`` is called **parameters**. It adheres to +a pre-defined set of parent/child propagation relationships and may be used as `Function Arguments`_ to each test section. -This feature is a supplement to static testcase data (attribute values stored -within each testcases). +This feature is a supplement to static test case data (attribute values stored +within each test case). .. tip:: @@ -43,10 +43,10 @@ Before delving deeper into the concept and mechanism of **parameters**, let's first spend some time on what it is, why they are needed, and how they may benefit the end user. -Parameters are a special kind of variable, used within functions and methods to -refer and access its input data (arguments). If you consider the function and -methods as the "doer", then parameters are the "what to do with". Eg, an -``add`` function would require 2 or more parameters to be "added together". +Parameters are a special kind of variable used within functions and methods to +refer to and access its input data (arguments). If you consider the function and +methods as the "doer," then parameters are the "what to do with." E.g., an +``add`` function would require two or more parameters to be "added together". .. code-block:: python @@ -74,33 +74,33 @@ methods as the "doer", then parameters are the "what to do with". Eg, an add_to_c(1, 2) # 103 -In a data-driven testing, testscripts are the *doers*, performing the act -of testing a facet of some product. Its arguments and parameters are thus the -input data that influences the specific acts of testing being carried out. Here +In data-driven testing, test scripts are the *doers*, performing the act +of testing a facet of some product. Test script arguments and parameters are thus the +input data that influences the specific actions of testing. Here are some possible use cases: - - ``testbed`` argument to a script tells it which testbed to run on, and + - The ``testbed`` argument to a script tells it which testbed to run on, and what the topology is like. The script can then connect to the testbed, and decide how to perform the testing best suited for this topology. - - ``vlan`` argument to ``layer2_traffic`` script can dynamically control the - vlan to be be configured for traffic testing. + - The ``vlan`` argument to the ``layer2_traffic`` script can dynamically control the + VLAN to be configured for traffic testing. - - other toggle arguments that dynamically turns on/off certain testcases, - and/or combination of features to be configured & tested + - Other toggle arguments that dynamically turn on/off certain test cases, + and a combination of features to be configured & tested - - etc. + - Etc. Of course, the parameters feature in ``aetest`` is much more than just script -arguments. It enables users to write testcases and testscripts that are capable +arguments. It enables users to write test cases and test scripts that are capable of being driven by inputs, varying the degree of testing, etc. Relationship Model ------------------ In ``aetest``, parameters are **relative**: parameters corresponding to each -object is the combination of its local specific parameters, and all of its -parent object's parameters. Eg: +object are the combination of its local specific parameters and all of its +parent object's parameters. E.g.: - ``Testcase`` parameters = local parameters + ``TestScript`` parameters @@ -113,9 +113,9 @@ runtime behaviors section: each object has a parent, and the parameters seen at each object level is an aggregation of its and all of its parent's parameters. -In case when an object and its parent (or its parent's parent, etc) have the same +In case when an object and its parent (or its parent's parent, etc.) have the same parameter names defined, then the parameter value closest to the current scope -is used/prefered. +is used/preferred. .. figure:: parameter_relations.png :align: center @@ -123,7 +123,7 @@ is used/prefered. *Parameter Relationship Model In a Nutshell* Below is a behavior demonstration of this relationship model. In actual script -execution, this happens behind-the-scenes automatically. +execution, this happens behind the scenes automatically. .. code-block:: python @@ -153,23 +153,23 @@ execution, this happens behind-the-scenes automatically. } # during runtime, the combined parameters seen at the - # testcase level, would be equivalent to the following: - # - take the testscript parameters as basis + # testcase level would be equivalent to the following: + # - take the testscript parameters as the basis # - and add to it, testcase parameters # - # eg: + #, e.g.: new_testcase_parameters = testscript.parameters.copy() new_testcase_parameters.update(testcase.parameters) testcase.parameters = new_testcase_parameters - # so that the new parameters seen at the testcase + # so that the new parameters seen in the test case # level, is: testcase.parameters # {'param_A': 100, 'param_B': 2, 'param_C': 3} .. hint:: - in other words, childs inherits but shadows parent parameters. This is + In other words, children inherit but shadow parent parameters. This is similar to Python `Variable Scoping`_ concept. Parameters Property @@ -178,7 +178,7 @@ Parameters Property Every top-level object in ``aetest`` comes with the special ``parameters`` property: a dictionary containing the key/value data pairs relative to this object (:ref:`object_model`). Its default values can be set/updated by the user -within the testscript. +within the test script. .. code-block:: python @@ -200,10 +200,10 @@ within the testscript. } # using Testcase to demonstrate TestContainer-type parameters definitions - # note that this also is applicable to CommonSetup & CommonCleanup + # note that this also applies to CommonSetup & CommonCleanup class Testcase(aetest.Testcase): - # all default parameters specific to this testcase is declared + # all default parameters specific to this test case are declared # in its own parameters dictionary. parameters = { 'generic_param_A': 200 @@ -212,7 +212,7 @@ within the testscript. # etc ... During runtime, these dictionaries form the baseline ``parameters`` properties -of their corresponding section. Eg: +of their corresponding section. E.g.: - script-level ``parameters`` dictionary is used to create ``TestScript`` object parameters. @@ -221,7 +221,7 @@ of their corresponding section. Eg: One exception to the above is method local parameters for sections such as ``subsection``, ``setup``, ``test`` and ``cleanup``. Even though their -corresponding classes (``Subection``, ``SetupSection``, ``TestSection``, +corresponding classes (``Subsection``, ``SetupSection``, ``TestSection``, ``CleanupSection``) also have the parameters property, these class instances only exists briefly during runtime (see :ref:`aetest_function_classes`), so their attributes are mostly only dynamic in nature, set & controlled by the @@ -235,9 +235,9 @@ dynamically access & update parameters. .. important:: - even though parameters seen at each object level also includes its parent's + Even though parameters seen at each object level also include its parent's parameters, setting & updating the parameters dictionary is only reflected - locally, and does not propagate to the parent. This is also inline with how + locally, and does not propagate to the parent. This is also in line with how Python `Variable Scoping`_ works. .. code-block:: python @@ -247,7 +247,7 @@ dynamically access & update parameters. # # continuing from the above - # re-defining the testcase for the sake of code-continuity + # re-defining the test case for the sake of code-continuity class Testcase(aetest.Testcase): # local parameters defaults, same as above @@ -258,7 +258,7 @@ dynamically access & update parameters. # within any sections, the parent container parameters are directly # accessible (applicable to setup/test/cleanup and subsections) - # here we'll do a combination access & updating of parameters + # here, we'll do a combination of access & updating of parameters @aetest.setup def setup(self): # add to the parameters dict @@ -272,7 +272,7 @@ dynamically access & update parameters. @aetest.test def test(self): - # access & print parent testscript parameters + # access & print parent test script parameters # (following the parent model) print(self.parent.parameters) # {'generic_param_A': 100, @@ -288,12 +288,11 @@ dynamically access & update parameters. # 'testscript_param_B': [], # 'testscript_param_A': 'another value'} - -Consider the above example: parameters can be set and acccessed as the script -runs, opening up the opportunity for scripts to dynamically discover the runtime -environment and modify test behavior (parameters) as required. Eg, ``setup`` -section of modifying testcase parameters based on current testbed state, and -altering the behavior of ensuiting ``test`` sections, etc. +Consider the above example: parameters can be set and accessed as the script +runs, opening the opportunity for scripts to dynamically discover the runtime +environment and modify test behavior (parameters) as required. E.g., the ``setup`` +section of modifying test case parameters based on the current testbed state, and +altering the behavior of ensuing ``test`` sections, etc. .. tip:: @@ -302,13 +301,12 @@ altering the behavior of ensuiting ``test`` sections, etc. .. _Collections.ChainMap: https://docs.python.org/3/library/collections.html#collections.ChainMap - .. _script_args: Script Arguments ---------------- -In short, any arguments passed to the testscript before startup becomes part of +In short, any arguments passed to the test script before startup becomes part of the ``TestScript`` parameter. This includes all the arguments passed through the :ref:`easypy_jobfile` during :ref:`aetest_jobfile_execution`, and/or any command line arguments parsed and passed to ``aetest.main()`` during @@ -320,12 +318,12 @@ line arguments parsed and passed to ``aetest.main()`` during # ------- # # script parameters and how it works - # (pseudo code, for demonstration only) + # (pseudo-code, for demonstration only) - # without going into details of how script parameters/arguments are + # without going into details about how script parameters/arguments are # passed in (covered under Running Scripts section) - # assuming that the testscript was called with the following inputs + # assuming that the test script was called with the following inputs script_arguments = { 'arg_a': 100, 'arg_c': 3, @@ -347,15 +345,15 @@ line arguments parsed and passed to ``aetest.main()`` during # 'arg_b': 2, # 'arg_c': 3} -As demonstrated in the above example, script arguments/parameters are actually -added on top of ``TestScript`` parameters defined within the script. In +As demonstrated in the above example, script arguments/parameters are +added on top of the ``TestScript`` parameters defined within the script. In other words, script arguments are added dynamically to the current running -script's base parameters, and overwrites any existing ones. +script's base parameters and overwrite existing ones. .. tip:: - define your default parameters in the script, and change the behavior of - the testscript by overwriting specific ones using script arguments. + Define your default parameters in the script, and change the behavior of + the test script by overwriting specific ones using script arguments. .. _parameters_as_funcargs: @@ -364,15 +362,15 @@ Parameters as Function Arguments ``parameters`` property & functionality provides a means for objects within ``aetest`` to follow the :ref:`parent` model and aggregate data together in a -clean, accessible format. It serves also as the basis for providing section -methods their `Function Arguments`_. This is the main mechanism behind the +clean, accessible format. It also serves as the basis for providing section +methods their `Function Arguments`_. This is the primary mechanism behind the data-driven concept of ``aetest``: function/methods are **driven** by input parameters. During runtime, all section method function arguments are filled by their corresponding parameter value, matching the argument name vs. the parameter key/name. The following table describes the types of function arguments and -their supports. +their support. .. csv-table:: Function Argument Types & Parameters Support :header: "Type", "Example", "Comments" @@ -404,9 +402,9 @@ their supports. class Testcase(aetest.Testcase): - # this setup section definition identifies "param_B" as - # as a input requirement. as this parameter is available at this - # testcase level (aggregated from parent testscript), it + # this setup section definition identifies "param_B" + # as an input requirement. As this parameter is available at this + # test case level (aggregated from the parent test script), it # is passed in as input @aetest.setup def setup(self, param_B): @@ -442,32 +440,31 @@ their supports. This is the preferred method of accessing parameters: by passing each in explicitly as function arguments. It is more pythonic: - - explictly passing in parameters makes the code (and its dependencies) + - Explicitly passing in parameters makes the code (and its dependencies) easier to read and understand - - allows the infrastructure to handle error scenarios (such as missing + - Allows the infrastructure to handle error scenarios (such as missing parameters) - - allows users to easily define defaults (without dealing with dictionary + - Allows users to easily define defaults (without dealing with dictionary operations) - - maintaining the ability to call each section as a function with various + - Maintaining the ability to call each section as a function with various arguments during test/debugging situations. - - etc... + - Etc. .. tip:: - once a parameter is passed into a section as a function argument, it becomes + Once a parameter is passed into a section as a function argument, it becomes a local variable. All rules of `Variable Scoping`_ apply. - Callable Parameters ------------------- -A callable parameter is a one that evaluates to ``True`` using callable_. When +A callable parameter is one that evaluates to ``True`` using callable_. When a callable parameter is filled as a function argument to test sections, the -infrastructure "calls" it, and uses its return value as the actual argument +infrastructure "calls" it and uses its return value as the actual argument parameter. .. code-block:: python @@ -490,65 +487,63 @@ parameter. class Testcase(aetest.Testcase): - # as the "number" parameter's value is the callable function + # As the "number" parameter's value is the callable function # random.random, this function is evaluated right before the # execution of this test method, and the call result is then used # as the actual argument input @aetest.test def test(self, number): - # test whether the generated number is greater than 0.5 + # Test whether the generated number is greater than 0.5 assert number > 0.5 - # if you run this test enough times + # If you run this test enough times # you will find that it passes exactly 50% of the time # assuming that random.random() generate truly random numbers :) - # note that callable parameters are only evaluated if used - # as function arguments. they are still objects if viewed - # through the parameters property + # Note that callable parameters are only evaluated if used + # as function arguments. Callable parameters are still objects + # if viewed through the parameters property self.parameters['number'] # - -Callable parameters still shows up as their original function object when -accessed through ``parameters`` property. They are only evaluated (called) when +Callable parameters still appear as their original function object when +accessed through the ``parameters`` property. They are only evaluated (called) when used as function arguments to test methods. This evaluation occurs "on demand": - - the evalution takes place right before method execution. + - The evaluation takes place right before method execution. - - each method gets its own indepedent evaluated result. + - Each method gets its independent evaluated result. The only limitation with callable parameters is that they cannot have arguments. ``aetest`` would not know how to fulfill them during runtime. .. tip:: - eliminate function arguments (by pre-setting them) with + Eliminate function arguments (by pre-setting them) with `partial functions`_. .. _callable: https://docs.python.org/3.4/library/functions.html#callable .. _partial functions: https://docs.python.org/3.4/library/functools.html#functools.partial - Parametrizing Functions ----------------------- Parametrized functions are a special breed of "smart" callable parameters. They -support arguments, are capable of identifying the current execution context, and -act accordingly. +support arguments, can identify the current execution context, and +can act accordingly. A parametrized function is declared when the ``@parameters.parametrize`` -decorator is used on a function in your testscript. This also adds the newly +decorator is used on a function within a test script. This also adds the newly created parametrized function automatically as part of ``TestScript`` parameters, using the function name as the parameter name. -During runtime, the behavior of these parametrized functions is exactly +During runtime, the behavior of these parametrized functions are identical to its callable parameter sibling, with the following additions: - - any arguments to the ``@parameters.parametrize`` decorator are stored + - Any arguments to the ``@parameters.parametrize`` decorator are stored and used as function arguments during the evaluation. - - if an argument named ``section`` is defined for a parametrized function, + - If an argument named ``section`` is defined for a parametrized function, the current section object is passed to the function. .. code-block:: python @@ -556,28 +551,27 @@ identical to its callable parameter sibling, with the following additions: # Example # ------- # - # parametrized function example + # Parametrized function example import random from pyats import aetest - # defining a parametrized function called "number" + # Defining a parametrized function called "number" # ------------------------------------------------ - # this function accepts a lower and an upper bound, and - # uses the random.randint() api to do the actual work. - # as part of this parametrization declaration, notice that - # a lower_bound and an upper_bound was provided. these values + # This function accepts a lower and an upper bound, and + # uses the random.randint() API to do the actual work. + # As part of this parametrization declaration, notice that + # a lower_bound and an upper_bound were provided. These values # are used as function arguments when the function is evaluated @aetest.parameters.parametrize(lower_bound=1, upper_bound=100) def number(lower_bound, upper_bound): return random.randint(lower_bound, upper_bound) - - # defining a parametrized function named "expectation" + # Defining a parametrized function named "expectation" # ---------------------------------------------------- - # this is a smart function: it can decide what to return + # This is a smart function: it can decide what to return # based on the current section object information. - # it accepts the current section as input, and + # The fucntion accepts the current section as input, and # returns 999 when the section uid is 'expected_to_pass', or 0 otherwise. @aetest.parameters.parametrize def expectation(section): @@ -586,49 +580,48 @@ identical to its callable parameter sibling, with the following additions: else return 0 - # as previously stated, there's no need to add parametrized functions + # As previously stated, there's no need to add parametrized functions # to the parameters dict(). They are automatically discovered and added. - # defining two tests under this testcase + # Defining two tests under this test case # ---------------------------------------------- - # similar to callable parameters, the above parameters + # Similar to callable parameters, the above parameters # are evaluated when used as function arguments to section # the only difference is the support for parameter function arguments. class Testcase(aetest.Testcase): - # this section is expected to pass + # This section is expected to pass # the generated number is between 1 and 100, and the # expectation is 9999 (as section uid is "expected_to_pass") @aetest.test def expected_to_pass(self, number, expectation): - # test whether expectation is > than generated number + # Test whether expectation is > than generated number assert expectation > number - # this section is expected to fail + # This section is expected to fail # the generated number is still between 1 and 100, but the # expectation is 0 (as section uid is not "expected_to_pass") @aetest.test def expected_to_fail(self, number, expectation): - # test whether expectation is > than generated number + # Test whether expectation is > than generated number assert expectation > number -Essentially, parametrized functions allows users to create smart, dynamic +Essentially, parametrized functions allow users to create smart, dynamic parameter values that can vary based on the current state of execution: by leveraging the :ref:`object_model` and :ref:`parent` relationship, the use cases are endless: - - return values based on current or parent section uid/type/result + - Return values based on current or parent section uid/type/result - - return values based on a combination of parameters available to the curent + - Return values based on a combination of parameters available to the current section - - etc. + - Etc. .. warning:: - when using ``section`` argument in parametrized function, the provided - section object is the same as the internal parameter described in the next - topic below. Try not to break stuff. + When using the ``section`` argument in the parametrized function, the provided + section object is the same as the internal parameter described in the following section. **Try not to break stuff.** .. _reserved_parameters: @@ -636,9 +629,9 @@ Reserved Parameters ------------------- Reserved parameters are those that are generated by the test infrastructure -during runtime. They are not normally seen when accessing the ``parameters`` +during runtime. They are generally not seen when accessing the ``parameters`` dictionary property, but are extremely useful when you need to refer to -``aetest`` internal objects that are normally unaccessible, and are needed when +``aetest`` internal objects that are normally inaccessible and are needed when using certain ``aetest`` optional features, such as :ref:`aetest_steps`. .. csv-table:: Current Reserved Parameters @@ -660,13 +653,12 @@ using certain ``aetest`` optional features, such as :ref:`aetest_steps`. internal: parameter offering access to AEtest internal objects feature: optional feature, enabled only when parameter is used as funcargs - Reserved parameters are special: they are only accessible if their name is provided as a keyword argument to test methods (or in the case of parametrized functions, ``section`` as a function input argument). They remain hidden in all other cases. -They are *reserved*: eg, they are resolved first and takes precedence over +They are *reserved*: e.g., they are resolved first and take precedence over normal parameters. In the case where a normal parameter is created with the same name, that parameter is only accessible using the ``parameters`` property, and is not useable as a function argument. @@ -676,72 +668,73 @@ property, and is not useable as a function argument. # Example # ------- # - # accessing reserved parameters + # Accessing reserved parameters from pyats import aetest - # using CommonSetup as an example + # Using CommonSetup as an example # also applicable to other TestContainer classes class CommonSetup(aetest.CommonSetup): - # create a local parameter with the same name + # Create a local parameter with the same name # as the reserved parameter parameters = { 'steps': object(), } - # access reserved parameters by providing their + # Access reserved parameters by providing their # names as keyword arguments to methods @aetest.subsection def subsection_one(self, testscript, section, steps): - # testscript object has an attribute called module - # which is this testscript's module + # Testscript object has an attribute called module + # which is this test script's module print (testscript.module) # - # current section object is Subsection + # Current section object is Subsection # and subsections have a unique uid print(section.uid) # subsection_one - # steps object enables the usages of steps + # The steps object enables the usage of steps with steps.start('a new demo step'): pass - # reserved parameters do not show up in **kwargs + # Reserved parameters do not show up in **kwargs @aetest.subsection def subsection_two(self, **kwargs): - # only the locally defined steps parameter show up + # Only the locally defined steps parameter shows up print(kwargs) # {'steps': } - # reserved paramters takes precedence when resolved. + # Reserved parameters take precedence when resolved. @aetest.subsection def subsection_three(self, steps): - # test steps is not the same as local paramter + # Test steps are not the same as local parameter steps is not self.parameters['steps'] # True - -Reserved parameters provides ``aetest`` a mechanism to offer optional features +Reserved parameters provide ``aetest`` a mechanism to offer optional features without polluting the :ref:`object_model` with additional attributes. It also -allows users to write testscripts that delve deeper and interact with the -internals of ``aetest`` using a supported method, instead of hacking around. +allows users to write test scripts that delve deeper and interact with the +internals of ``aetest`` using a supported method instead of hacking around. - *With great power, comes great responsibilities* - use them wisely. + *With great power comes great responsibilities* - use them wisely. .. tip:: - there is no reserved parameter for the current ``TestContainer``, as the + There is no reserved parameter for the current ``TestContainer``, as the class instance is naturally provided to bound methods as the first - argument (eg, ``self``). + argument (e.g., ``self``). .. warning:: - modifying internal parameters without knowing what you're doing may result - in catastrophic failures, and/or inexplicable script behaviors. + Modifying internal parameters without knowing what you're doing may result + in catastrophic failures and inexplicable script behaviors. Monkey patching internals is strictly prohibited. Doing so will void your warranty: **no further support will be provided.** + + From c1460a319e140f7c67854d59e8cbd738dec105df Mon Sep 17 00:00:00 2001 From: atestini Date: Fri, 22 Jul 2022 16:13:51 -0400 Subject: [PATCH 60/61] Improve readability and wording for Looping section --- docs/aetest/loop.rst | 335 +++++++++++++++++++++---------------------- 1 file changed, 165 insertions(+), 170 deletions(-) diff --git a/docs/aetest/loop.rst b/docs/aetest/loop.rst index 9f751f3..0a5ded3 100644 --- a/docs/aetest/loop.rst +++ b/docs/aetest/loop.rst @@ -12,7 +12,6 @@ Looping Sections - `Yield Expressions`_ - `Factory Design`_ - .. _Decorators: https://wiki.python.org/moin/PythonDecorators .. _Classes Tutorial: https://docs.python.org/3.4/tutorial/classes.html .. _Scopes and Namespaces: https://docs.python.org/3.4/tutorial/classes.html#python-scopes-and-namespaces @@ -22,102 +21,104 @@ Looping Sections *What are loops? Refer to the end of this section.* -As an integral extension of :ref:`test_parameters` data-driven testing concept, -``aetest`` also supports section looping: reusing section code body by providing +As an integral extension of the :ref:`test_parameters` data-driven testing concept, +``aetest`` also supports section looping. Section looping means reusing a section's code body by providing it with different parameters during each loop iteration. The following describes each section and their loop capability and behaviors: ``CommonSetup``/``CommonCleanup`` - Common setup and cleanup sections are unique within each testscript. They - are run only once per testscript execution, and are not loopable. + Common setup and cleanup sections are unique within each test script. They + are run only once per test script execution, and are not loopable. ``subsection`` Subsections within ``CommonSetup`` and ``CommonCleanup`` are loopable. - When a ``subsection`` is marked for looping, each of its iterations is + When a ``subsection`` is marked for looping, each iteration is reported as a new subsection. ``Testcase`` Testcases are loopable. Each iteration of a looping ``Testcase`` is reported - individually as new testcase instances with different ``uid``. When a - ``Testcase`` is looped, all of its contents (setup, tests and cleanup) are + individually as new test case instances with a different ``uid``. When a + ``Testcase`` is looped, and all of its contents (setup, tests, and cleanup) are run fully per each iteration. ``setup``/``cleanup`` - Setup and cleanup sections within each testcase is unique, and are run + Setup and cleanup sections within each test case is unique, and are run only once per ``Testcase``. They cannot be looped individually, but if their parent ``Testcase`` is looped, then they are run once per ``Testcase`` iteration. ``test`` Test sections within ``Testcase`` are loopable individually. Each - iteration has its own unique id, and is reported as a new test - section. When a looping ``test`` section's parent ``Testcase`` is also - looped, the resulting loops are multiplicative. Eg: if a testcase is - looped ``2x``, and contains a test that is also looped ``2x``, that - test would loop ``2x`` per testcase loop iteration. + iteration has its own unique id and is reported as a new test + section. When a looping ``test`` section's parent ``Testcase`` is + looped, the resulting loops are multiplicative. E.g.: if a test case is + looped ``2x``and contains a test that is also looped ``2x``, that + test would loop ``2x`` per test case loop iteration. .. hint:: - in other words, ``subsection``, ``Testcase`` and ``test`` sections are the + In other words, ``subsection``, ``Testcase`` and ``test`` sections are the only loopable sections. - Defining Loops -------------- -Sections are marked for looping when they are decorated with ``@loop``, and its -looping parameters provided as decorator arguments. During runtime, when +Sections are marked for looping when decorated with ``@loop``, and its +looping parameters are provided as decorator arguments. During runtime, when ``aetest`` infrastructure detects looped section code, their corresponding -section object is then instantiated once for each of its iterations. +section object is then instantiated once for each iteration. .. code-block:: python # Example # ------- # - # defining loops on sections + # Defining loops on sections from pyats import aetest - # defining a common setup section - # contains a subsection that is looped twice. + # Defining a common setup section + # The CommonSetup section contains a + # subsection that is looped twice. class CommonSetup(aetest.CommonSetup): - # defining a subsection - # this subsection is marked to be looped twice - # the first time having a uid of "subsection_one", and + # Defining a subsection + # ------------------------ + # This subsection is marked to be looped twice. + # The first time having a uid of "subsection_one", and # the second time having a uid of "subsection_two" @aetest.loop(uids=['subsection_one', 'subsection_two']) @aetest.subsection def looped_subsection(self): pass - # defining a testcase that loops - # this testcase also contains a test section that is looped twice + # Defining a test case that loops + # ---------------------------------- + # This test case also contains a test section that is looped twice @aetest.loop(uids=['testcase_one', 'testcase_two']) class Testcase(aetest.Testcase): - # setup section of this testcase is run once - # every time the testcase is looped. + # Setup section of this test case is run once + # every time the test case is looped. @aetest.setup def setup(self): pass - # looped test section - # both iterations are run per testcase iteration + # Looped test section + # both iterations are run per test case iteration @aetest.loop(uids=['test_one', 'test_two']) @aetest.test def test(self): pass - # cleanup section of this testcase is run once - # every time the testcase is looped. + # Cleanup section of this test case is run once + # every time the test case is looped. @aetest.cleanup def cleanup(self): pass - # this testscript's resulting sections would look like this below + # This test script's resulting sections would look like this below # # SECTIONS/TESTCASES RESULT # ---------------------------------------------------------------------- @@ -136,22 +137,22 @@ section object is then instantiated once for each of its iterations. # |-- test_two PASSED # `-- cleanup PASSED -As shown above, the minimum requirement to loop a section (eg, to run its code -1+ times) is to decorate the section with ``@loop``, and provide a list of loop -iteration uids using ``uids`` argument. This controls the number of iterations -this section is looped: each unique item in the ``uids`` list generates to +As shown above, the minimum requirement to loop a section (e.g., to run its code +1+ times) is to decorate the section with ``@loop``and provide a list of loop +iteration uids using the ``uids`` argument. This controls the number of iterations +this section is looped: Each unique item in the ``uids`` list generates a new section with that uid. -When ``@loop`` is used on a ``@subsection`` or ``@test``, the section method +When the ``@loop`` decorator is used on a ``@subsection`` or ``@test``, the section method is effectively decorated twice, and even though the order does not matter, it make more sense to use ``@loop`` as the outermost decorator, signifying that -this method is first marked as a section, then this section is looped. +this method is first marked as a section; then this section is looped. .. tip:: - decorators are executed in the order of "innermost" to "outermost". + Decorators are executed from "innermost" to "outermost." -In addition, in an effort to make the script more aesthetically pleasing, +Additionally, to make the script more aesthetically pleasing, ``aetest`` also features a shortcut to avoid the double decorators: ``@subsection.loop`` and ``@test.loop``. @@ -160,20 +161,20 @@ In addition, in an effort to make the script more aesthetically pleasing, # Example # ------- # - # demonstration the double decorator shortcut for test and subsections + # Demonstrating the double decorator shortcut for tests and subsections from pyats import aetest class CommonSetup(aetest.CommonSetup): - # marking this as both a subsection, and being looped + # Marking this as both a subsection and being looped @aetest.subsection.loop(uids=['subsection_one', 'subsection_two']) def looped_subsection(self): pass class Testcase(aetest.Testcase): - # marking this as both a test section and being looped + # Marking this as both a test section and being looped @aetest.test.loop(uids =['test_one', 'test_two']) def test(self): pass @@ -187,24 +188,23 @@ In addition, in an effort to make the script more aesthetically pleasing, .. tip:: - python ``@decorators`` are evaluated at import time. Thus, decorator - arguments may only be static. If you need to reference runtime and/or - dynamic information information as part of your loop declaration, eg, - accessing parameters & etc, refer to :ref:`dynamic_looping`. - + Python ``@decorators`` are evaluated at import time. Thus, decorator + arguments may only be static. If you need to reference runtime and + dynamic information information as part of your loop declaration, e.g., + accessing parameters, etc., refer to :ref:`dynamic_looping`. Loop Parameters --------------- -Looping the same section again and again is not very useful. Even if each -section has a unique uid as demonstrated above, the usefulness of a test -that repeatedly perform the same actions is questionable. This is where **loop +Looping the same section, again and again, is not very useful. Even if each +section has a unique uid, as demonstrated above, the usefulness of a test +that repeatedly performs the same actions is questionable. This is where **loop parameters** comes in. -Loop parameters feature allows each loop iteration to receive new, distinct +The loop parameters feature allows each loop iteration to receive new, distinct :ref:`test_parameters`. These parameters are specified as part of the ``@loop`` decorator, processed and propagated to each section instance as their -*local parameters*. Combined with :ref:`parameters_as_funcargs` feature, each +*local parameters*. Combined with the :ref:`parameters_as_funcargs` feature, each looped section is then driven to potentially do something different. .. code-block:: python @@ -212,25 +212,24 @@ looped section is then driven to potentially do something different. # Example # ------- # - # loop parameters demonstration + # Loop parameters demonstration from pyats import aetest - # loop this testcast with a loop parameter named "a" + # Loop this test case with a loop parameter named "a" # and set it to value 2 for the first iteration, # and 3 for the second iteration @aetest.loop(a=[2, 3]) class Testcase(aetest.Testcase): - # loop this test with loop parameter named "b" - # and set it to 8 for the first iteration, 9 for the second. + # Loop this test with a loop parameter named "b" + # and set it to 8 for the first iteration and 9 for the second. @aetest.test.loop(b=[8, 9]) def test(self, a, b): # this test prints the exponential of two inputs, a and b print("%s ^ %s = %s" % (a, b, a**b)) - - # the output of the testcase would look like this: + # The output of the test case would look like this: # 2 ^ 8 = 256 # 2 ^ 9 = 512 # 3 ^ 8 = 6561 @@ -249,22 +248,22 @@ looped section is then driven to potentially do something different. # |-- test[b=8] PASSED # `-- test[b=9] PASSED -In effect, loop parameters allows users to create and/or modify the looped -section's local parameters on the fly, per iteration. It is an extension of the -dynamic parameter concept, where section parameters are being generated and fed +In effect, loop parameters allow users to create and modify the looped +section's local parameters on the fly per iteration. It is an extension of the +dynamic parameter concept, where section parameters are generated and fed to each section during runtime. -The use of loop parameters also makes ``uids`` argument optional: if ``uids`` +The use of loop parameters also makes the ``uids`` argument optional: If the ``uids`` arguments are not provided, the infrastructure generates unique section uids by combining the original section name with each of its current loop parameters as postfix -in square backets. Otherwise, the provided ``uids`` are used as section uids. +in square brackets. Otherwise, the provided ``uids`` are used as section uids. There are two methods of providing loop parameters to the ``@loop`` decorator: - - by providing a list of parameters, and a list of parameter values for + - By providing a list of parameters, and a list of parameter values for each iteration (eg, using ``args`` and ``argvs``) - - by providing each parameter as a keyword argument, and a list of its + - By providing each parameter as a keyword argument, and a list of its corresponding argument values. (eg, ``a=[1, 2, 3], b=[4, 5, 6]``) .. code-block:: python @@ -272,22 +271,22 @@ There are two methods of providing loop parameters to the ``@loop`` decorator: # Example # ------- # - # providing loop parameters + # Providing loop parameters from pyats import aetest class Testcase(aetest.Testcase): - # loop this test with arguments "a", "b", and "c" - # provide all of its iteration arguments together using method one - # the positions of each value in argvs corresponds to its args name + # Loop this test with arguments "a", "b", and "c". + # Provide all of its iteration arguments together using method one. + # The positions of each value in argvs correspond to its args name. @aetest.test.loop(args=('a', 'b', 'c'), argvs=((1, 2, 3), (4, 5, 6))) def test_one(self, a, b, c): print("a=%s, b=%s, c=%s" % (a, b, c)) - # loop this test with the same arguments as above, but + # Loop this test with the same arguments as above, but # provide each of its iteration arguments independently using method two @aetest.test.loop(a=(1,4), b=(2,5), @@ -296,7 +295,7 @@ There are two methods of providing loop parameters to the ``@loop`` decorator: print("a=%s, b=%s, c=%s" % (a, b, c)) - # testcase output: + # Testcase output: # a=1, b=2, c=3 # a=4, b=5, c=6 # a=1, b=2, c=3 @@ -311,25 +310,25 @@ There are two methods of providing loop parameters to the ``@loop`` decorator: # |-- test_two[a=1,b=2,c=3] PASSED # `-- test_two[a=4,b=5,c=6] PASSED -As shown above, there were no difference in the outcome of the results. The only +As shown above, there were no differences in the outcome of the results. The only difference was how the loop parameters were provided. One method may be superior -to the other depending on the situation, the number of arguments, etc. +to the other depending on the use case, the number of arguments, etc. -When using loop parameters, the following rules determines the actual number +When using loop parameters, the following rules determine the actual number of iterations: - - if ``uids`` were provided, the number of iterations is equal to the number + - If ``uids`` arguments were provided, the number of iterations is equal to the number of ``uids`` provided. - - if the number of parameter values exceeds the number of ``uids``, all + - If the number of parameter values exceeds the number of ``uids``, all extra values are discarded. - - if ``uids`` are not provided, the number of iterations is equal to the + - If ``uids`` arguments are not provided, the number of iterations equals the number of loop parameter values. Eg, if ``@loop(a=[1,2,3])``, then there would be 3 loop instances, each taking on one distinct value: ``a=1``, ``a=2``, ``a=3``. - - if there are multiple parameters and the number of their values do not + - If there are multiple parameters and the number of their values do not agree, or if the number of parameter values is less than the number of provided ``uids``, a ``filler`` is used to fill empty spots. ``filler`` defaults to ``None``, and only 1 filler can be provided. @@ -339,26 +338,26 @@ of iterations: # Example # ------- # - # loop parameter combinations - # (pseudo code for demonstration only) + # Loop parameter combinations + # (pseudo-code for demonstration only) from pyats.aetest import loop - # loop with 2 iterations using uids argument + # Loop with 2 iterations using uids argument # ------------------------------------------ # iteration 1: uid='id_one' # iteration 2: uid='id_two' @loop(uids=['id_one', 'id_two']) - # loop with 2 iterations using parameters argument + # Loop with 2 iterations using parameters argument # ------------------------------------------------ # iteration 1: a=1, b=4 # iteration 2: a=2, b=5 @loop(a = [1, 2], b = [4, 5]) - # same as above, using args and argvs + # Same as above, using args and argvs @loop(args=['a', 'b'], argvs=[(1, 4), (2, 5)]) - # loop with 2 iterations, and extra arguments are discarded due to uids + # Loop with 2 iterations, and extra arguments are discarded due to uids # --------------------------------------------------------------------- # iteration 1: uid='id_one', a=1, b=2 # iteration 2: uid='id_two', a=3, b=4 @@ -368,23 +367,23 @@ of iterations: argvs=[(1, 2), (3, 4), (5, 6)]) - # same example as above but using per-parameter values + # Same example as above but using per-parameter values @loop(uids=['id_one', 'id_two'], a=[1, 3, 5], b=[2, 4, 6]) - # loop with 3 iterations, and their number of parameters values do not agree + # Loop with 3 iterations, and their number of parameters values do not agree # -------------------------------------------------------------------------- # iteration 1: a=1, b=4 # iteration 2: a=2, b=5 # iteration 3: a=3, b=None ---> default filler comes in to fill the blanks @loop(a=[1, 2, 3], b=[4, 5]) - # same as above, using args and argvs + # Same as above, using args and argvs @loop(args=['a', 'b'], argvs=[(1, 4), (2, 5), (3, )]) - # loop with more uids than parameters, and custom filler + # Loop with more uids than parameters, and custom filler # ------------------------------------------------------ # iteration 1: uid='id_one', a=1, b=3 # iteration 2: uid='id_two', a=2, b=4 @@ -399,13 +398,13 @@ Advanced Loop Usages -------------------- Arguments to the ``@loop`` decorator may also be `callable`_, `iterable`_, or a -`generator`_. The infrastructure is able to distinguish and treat each as you +`generator`_. The infrastructure can distinguish and treat each as you would normally expect it to: - - if an argument value is a `callable`_, it is called, and its returns + - If an argument value is a `callable`_, it is called, and its returns are then used as the actual loop argument value. - - if an argument value is an `iterable`_ or a `generator`_, the loop engine + - If an argument value is an `iterable`_ or a `generator`_, the loop engine picks only one element from it at a time to build the next iteration, until it is exhausted. @@ -418,18 +417,18 @@ would normally expect it to: # Example # ------- # - # demonstrating advanced loop parameter behaviors + # Demonstrating advanced loop parameter behaviors from pyats import aetest - # defining a function - # functions are callable + # Defining a function + # Functions are callable def my_function(): value = [1, 2, 3] print("returning %s" % value) return value - # defining a generator + # Defining a generator def my_generator(): for i in [4, 5, 6]: print('generating %s' % i) @@ -437,20 +436,20 @@ would normally expect it to: class Testcase(aetest.Testcase): - # creating test section with parameter "a" as a function - # note that the function object is passed, not its values + # Creating test section with parameter "a" as a function + # Note that the function object is passed, not its values @aetest.test.loop(a=my_function) def test_one(self, a): print("a = %s" % a) - # creating a test section with parameter "b" as a generator - # note that the generator is a result of calling my_generator(), not + # Creating a test section with parameter "b" as a generator + # Note that the generator is a result of calling my_generator(), not # the function itself. @aetest.test.loop(b=my_generator()) def test_two(self, b): print('b = %s' % b) - # the output of the testcase would be: + # The output of the test case would be: # returning [1, 2, 3] # a = 1 # a = 2 @@ -470,31 +469,30 @@ In the above example, pay close attention to the output lines: - Iterators and generators are only queried before the next section needs to be created. -This behavior enables the use of custom generator as input values to your loop +This behavior enables using a custom generator as input values to your loop parameters. For example, a generator state machine that queries the current testbed device status and creates iterations based on that information. Since the generator is not polled until right before the next iteration, your custom function is only run in-between test sections, thus dynamically generating the -loop iterations based current test environments. - +loop iterations based on current test environments. .. _dynamic_looping: Dynamic Loop Marking -------------------- -So far, all loop examples focused on defining ``@loop`` directly within the -testscripts. Eg, the ``@loop`` decorators are coded as part of the testscript. -In addition, it is also possible to dynamically mark sections for looping during -runtime, eg, creating loops based on information that is only available during -a script run. To do this, use the ``loop.mark()`` function. +So far, all loop examples focus on defining the ``@loop`` decorator directly within the +test scripts. E.g., the ``@loop`` decorators are coded as part of the test script. +However, it is also possible to dynamically mark sections for looping during +runtime, e.g., creating loops based on information that is only available during +a script’s run. To do this, use the ``loop.mark()`` function. .. code-block:: python # Example # ------- # - # dynamically marking sections for looping + # Dynamically marking sections for looping from pyats import aetest @@ -502,23 +500,22 @@ a script run. To do this, use the ``loop.mark()`` function. @aetest.setup def setup(self): - # mark the next test for looping - # provide it with two unique test uids. + # Mark the next test for looping + # Provide it with two unique test uids. # (self.simple_test is the next test method) aetest.loop.mark(self.simple_test, uids=['test_one', 'test_two']) - # note: the simple_test section is not directly marked for looping + # Note: the simple_test section is not directly marked for looping # instead, during runtime, its testcase's setup section marks it for # looping dynamically. @aetest.test def simple_test(self, section): - # print the current section uid + # Print the current section uid # by using the internal parameter "section" print("current section: %s" % section.uid) - - # output of this testcase + # Output of this test case # current section: test_one # current section: test_two # @@ -530,20 +527,19 @@ a script run. To do this, use the ``loop.mark()`` function. # |-- test_one PASSED # `-- test_two PASSED -``loop.mark()`` arguments & behaviors (including loop parameters & etc) are -exactly identical to its sibling ``@loop`` decorator, with the only exception -that its first input argument must be the target section method/class. Eg: +``loop.mark()`` arguments and behaviors (including loop parameters, etc.) are +exactly identical to its sibling, the ``@loop`` decorator, with the only exception +that its first input argument must be the target section method/class. E.g.: ``loop.mark(Testcase_Two, a=[1,2,3])``. -The benefit of this approach is simple: dynamic information, parameters and -variables such as :ref:`script_args`, :ref:`parent` etc, are only available +The benefit of this approach is simple: Dynamic information, parameters and +variables such as :ref:`script_args`, :ref:`parent` etc., are only available during runtime. This information and its corresponding variables are not available when the script is written, and delaying variable references (while -using ``@loop`` decorator) in Python is very difficult, if not impossible. +using the ``@loop`` decorator) in Python is very difficult, if not impossible. -------------------------------------------------------------------------------- - Loop Internals -------------- @@ -552,61 +548,60 @@ Loop Internals The information here onwards is for users interested in ``aetest`` internals & extensions only. - If you are new to this, do not read on. These advanced topics may - further fuel your confusion. + **If you are new to this, do not read on. These advanced topics may + further fuel your confusion.** The previous sections focused on the "how to use" aspect of ``aetest`` looping functionality. From here onwards, we'll dig deeper into loop internals, look at how it functions, and how to deviate from its default behaviors. -The ``aetest`` looping behavior & how its arguments are processed is actually +The ``aetest`` looping behavior and how its arguments are processed are highly customizable. This was not highlighted in previous sections for the sake of serializing the training & simplifying the learning curve. -In reality, consider ``@loop`` decorator and ``loop.mark()`` function as only -markers: they only mark the given section for looping. The details (parameters) -of each iterations is actually generated from **loop generators**, where all -arguments to ``@loop`` and ``loop.mark()`` propagates to. Eg: +In reality, consider the ``@loop`` decorator and ``loop.mark()`` function as only +markers: They only mark the given section for looping. The details (parameters) +Each iteration is generated from **loop generators**, where all +arguments to ``@loop`` and ``loop.mark()``propagate to. E.g.: .. code-block:: python # Example # ------- # - # pseudo code demonstrating @loop decorator functionality + # Pseudo-code demonstrating @loop decorator functionality - # what the loop decorator definition sort-of looks like - # note where the generator defaults to "DefaultLooper" + # What the loop decorator definition sort of looks like + # Note where the generator defaults to "DefaultLooper" def loop(generator=DefaultLooper, *args, **kwargs): - # the actual loop generator gets called with all of the arguments + # The actual loop generator gets called with all of the arguments # to loop decorator, and generates each section iteration return generator(*args, **kwargs) - # pseudo code here onwards, demonstrating internals + # Pseudo-code here onwards, demonstrating internals # ------------------------------------------------- # - # during runtime, the looped is expanded to create each iteration + # During runtime, the looped is expanded to create each iteration for iteration in loop(*args, **kwargs): - # create a section from iteration information and run it + # Create a section from iteration information and run it # ... - # eg, instantiate Subsection + #E.g., instantiate Subsection subsection = Subsection(uid=iteration.uid, parameters=iteration.parameters) # and add to common setup's subsections list common_setup.subsections.append(subsection) - # etc ... - + # etc. -Behind the scenes, **loop generators** are the actual classes that does the -heavy lifting: creating each iteration based on ``@loop`` and ``loop.mark()`` +Behind the scenes, **loop generators** are the actual classes that do the +heavy lifting: Creating each iteration based on the ``@loop`` and ``loop.mark()`` decorator arguments. Loop generators are `iterable`_. Each of its returned -member is an instance of ``Iteration`` class, containing the uid & parameters -information unique to this loop, and used by the infrastructure to create the +members is an instance of the ``Iteration`` class, containing the uid & parameters +information unique to this loop and used by the infrastructure to create the next section instance. .. csv-table:: Iteration class (collections.namedtuple) @@ -619,61 +614,61 @@ next section instance. the next looped section" In other words, **loop generator** is the object that ultimately controls how -loops are generated, and what parameters each iteration is associated with. The -looping behavior and arguments described in topics above are actually that of +loops are generated and what parameters each iteration is associated with. The +looping behavior and arguments described in the topics above are that of ``DefaultLooper``, the default **loop generator** provided by ``aetest`` loop infrastructure. Its features are sufficient for most use cases. However, if you -wish to customize loop behavior, it is possible to extend and/or override it. +wish to customize loop behavior, it is possible to extend and override it. .. code-block:: python # Example # ------- # - # demonstrating how to write and pass your own loop generator + # Demonstrating how to write and pass your own loop generator - # loop generators must return Iterations + # Loop generators must return Iterations from pyats import aetest from pyats.aetest.loop import Iteration - # let's write a custom loop generator - # it generates integers between a and b as loop iterations - # and pass the integer as "number" parameter of the executed section. - # each iteration uid is named "iteration_uid" + number + # Let's write a custom loop generator + # It generates integers between a and b as loop iterations + # and pass the integer as the "number" parameter of the executed section. + # Each iteration uid is named "iteration_uid" + number class DemoGenerator(object): - # at minimum, the loop generator needs to accept an argument called + # At a minimum, the loop generator needs to accept an argument called # "loopee", which is the actual object being looped. This allows the - # loop generator to know what it is looping on, and build information + # loop generator to know what it is looping on and build information # based on it. - # in this example, we're ignoring that argument, as our loop genrator - # is simple and straightforward. + # In this example, we're ignoring that argument, as our loop generator + # is straightforward. def __init__(self, loopee, a, b): self.numbers = list(range(a, b)) def __iter__(self): for i in self.numbers: - # each generated member is an instance of Iteration - # each Iteration must have a unique id + # Each generated member is an instance of Iteration + # Each Iteration must have a unique id # and all of its parameters stored in a dictionary yield Iteration(uid='iteration_uid_%s' % i, parameters={'number': i}) - # this loop generator can be used as @loop and loop.mark() argument. - # let's define a looped testcase with it. + # This loop generator can be used as the @loop and loop.mark() argument. + # Let's define a looped test case with it. - # looping this testcase with custom generator, and a=1, b=5 + # Looping this test case with a custom generator, and a=1, b=5 @aetest.loop(generator=DemoGenerator, a=1, b=5) class Testcase(aetest.Testcase): - # since our generator generates a parameter named "number" - # let's print it in this simple test. + # Since our generator generates a parameter named "number" + # Let's print it in this simple test. @aetest.test def test(self, number): print('current number: %s' % number) - # output of this testcase + # Output of this test case # current number: 1 # current number: 2 # current number: 3 @@ -693,14 +688,14 @@ wish to customize loop behavior, it is possible to extend and/or override it. .. hint:: - the above examples may be simple, the demonstrated underlying principles + The above examples may be simple, but the demonstrated underlying principles are not. *"Do not try and bend the spoon. That's impossible. Instead... only try to realize the truth..."* -And voila. Custom **loop generators** like above is immensely powerful: by -extending and/or overriding the default loop generation behavior, and defining +And voila. Custom **loop generators** like the above are immensely powerful: By +extending and overriding the default loop generation behavior, and defining custom test sections entirely driven by parameter inputs, users can effectively overload the loop functionality into a dynamic generator of highly abstracted test executor. From 4b152ee1c8009faed467739fb6dfbf42d0f880e5 Mon Sep 17 00:00:00 2001 From: Michael Bear <38406045+mjbear@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:04:48 -0500 Subject: [PATCH 61/61] Update Testcase/TestScript words in loop.rst Where it's clear the words are referring to Python objects: * test case > Testcase * test script > TestScript --- docs/aetest/loop.rst | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/aetest/loop.rst b/docs/aetest/loop.rst index 0a5ded3..ab4698e 100644 --- a/docs/aetest/loop.rst +++ b/docs/aetest/loop.rst @@ -28,8 +28,8 @@ it with different parameters during each loop iteration. The following describes each section and their loop capability and behaviors: ``CommonSetup``/``CommonCleanup`` - Common setup and cleanup sections are unique within each test script. They - are run only once per test script execution, and are not loopable. + Common setup and cleanup sections are unique within each TestScript. They + are run only once per TestScript execution, and are not loopable. ``subsection`` Subsections within ``CommonSetup`` and ``CommonCleanup`` are loopable. @@ -38,12 +38,12 @@ The following describes each section and their loop capability and behaviors: ``Testcase`` Testcases are loopable. Each iteration of a looping ``Testcase`` is reported - individually as new test case instances with a different ``uid``. When a + individually as new ``Testcase`` instances with a different ``uid``. When a ``Testcase`` is looped, and all of its contents (setup, tests, and cleanup) are run fully per each iteration. ``setup``/``cleanup`` - Setup and cleanup sections within each test case is unique, and are run + Setup and cleanup sections within each Testcase is unique, and are run only once per ``Testcase``. They cannot be looped individually, but if their parent ``Testcase`` is looped, then they are run once per ``Testcase`` iteration. @@ -52,9 +52,9 @@ The following describes each section and their loop capability and behaviors: Test sections within ``Testcase`` are loopable individually. Each iteration has its own unique id and is reported as a new test section. When a looping ``test`` section's parent ``Testcase`` is - looped, the resulting loops are multiplicative. E.g.: if a test case is - looped ``2x``and contains a test that is also looped ``2x``, that - test would loop ``2x`` per test case loop iteration. + looped, the resulting loops are multiplicative. E.g.: if a Testcase is + looped ``2x`` and contains a test that is also looped ``2x``, that + test would loop ``2x`` per Testcase loop iteration. .. hint:: @@ -93,27 +93,27 @@ section object is then instantiated once for each iteration. def looped_subsection(self): pass - # Defining a test case that loops + # Defining a Testcase that loops # ---------------------------------- - # This test case also contains a test section that is looped twice + # This Testcase also contains a test section that is looped twice @aetest.loop(uids=['testcase_one', 'testcase_two']) class Testcase(aetest.Testcase): - # Setup section of this test case is run once - # every time the test case is looped. + # Setup section of this Testcase is run once + # every time the Testcase is looped. @aetest.setup def setup(self): pass # Looped test section - # both iterations are run per test case iteration + # both iterations are run per Testcase iteration @aetest.loop(uids=['test_one', 'test_two']) @aetest.test def test(self): pass - # Cleanup section of this test case is run once - # every time the test case is looped. + # Cleanup section of this Testcase is run once + # every time the Testcase is looped. @aetest.cleanup def cleanup(self): pass @@ -216,7 +216,7 @@ looped section is then driven to potentially do something different. from pyats import aetest - # Loop this test case with a loop parameter named "a" + # Loop this Testcase with a loop parameter named "a" # and set it to value 2 for the first iteration, # and 3 for the second iteration @aetest.loop(a=[2, 3]) @@ -229,7 +229,7 @@ looped section is then driven to potentially do something different. # this test prints the exponential of two inputs, a and b print("%s ^ %s = %s" % (a, b, a**b)) - # The output of the test case would look like this: + # The output of the Testcase would look like this: # 2 ^ 8 = 256 # 2 ^ 9 = 512 # 3 ^ 8 = 6561 @@ -449,7 +449,7 @@ would normally expect it to: def test_two(self, b): print('b = %s' % b) - # The output of the test case would be: + # The output of the Testcase would be: # returning [1, 2, 3] # a = 1 # a = 2 @@ -482,7 +482,7 @@ Dynamic Loop Marking -------------------- So far, all loop examples focus on defining the ``@loop`` decorator directly within the -test scripts. E.g., the ``@loop`` decorators are coded as part of the test script. +TestScripts. E.g., the ``@loop`` decorators are coded as part of the TestScript. However, it is also possible to dynamically mark sections for looping during runtime, e.g., creating loops based on information that is only available during a script’s run. To do this, use the ``loop.mark()`` function. @@ -506,7 +506,7 @@ a script’s run. To do this, use the ``loop.mark()`` function. aetest.loop.mark(self.simple_test, uids=['test_one', 'test_two']) # Note: the simple_test section is not directly marked for looping - # instead, during runtime, its testcase's setup section marks it for + # instead, during runtime, its Testcase's setup section marks it for # looping dynamically. @aetest.test @@ -515,7 +515,7 @@ a script’s run. To do this, use the ``loop.mark()`` function. # by using the internal parameter "section" print("current section: %s" % section.uid) - # Output of this test case + # Output of this Testcase # current section: test_one # current section: test_two # @@ -656,9 +656,9 @@ wish to customize loop behavior, it is possible to extend and override it. # This loop generator can be used as the @loop and loop.mark() argument. - # Let's define a looped test case with it. + # Let's define a looped Testcase with it. - # Looping this test case with a custom generator, and a=1, b=5 + # Looping this Testcase with a custom generator, and a=1, b=5 @aetest.loop(generator=DemoGenerator, a=1, b=5) class Testcase(aetest.Testcase): @@ -668,7 +668,7 @@ wish to customize loop behavior, it is possible to extend and override it. def test(self, number): print('current number: %s' % number) - # Output of this test case + # Output of this Testcase # current number: 1 # current number: 2 # current number: 3