Abstracted Stages

Abstracted Stages allow us to reuse existing code and/or change the order of execution with minimal coding required.

Parent class (IOSXE ChangeBootVariable)

This is the parent class we will inherit for our cat9k implementation.

  1from genie.metaparser.util.schemaengine import Optional
  2from genie.libs.clean import BaseStage
  3
  4class ChangeBootVariable(BaseStage):
  5    """This stage configures boot variables of the device using the following steps:
  6
  7    - Delete existing boot variables.
  8    - Configure boot variables using the provided 'images'.
  9    - Set the configuration-register using the provided 'config_register'.
 10    - Write memory.
 11    - Verify the boot variables are as expected.
 12    - Verify the configuration-register is as expected.
 13
 14Stage Schema
 15------------
 16change_boot_variable:
 17
 18    images (list): Image files to use when configuring the boot variables.
 19
 20    timeout (int, optional): Execute timeout in seconds. Defaults to 300.
 21
 22    config_register (str, optional): Value to set config-register for
 23        reload. Defaults to 0x2102.
 24
 25    current_running_image (bool, optional): Set the boot variable to the currently
 26        running image from the show version command instead of the image provided.
 27        Defaults to False.
 28
 29Example
 30-------
 31change_boot_variable:
 32    images:
 33        - harddisk:/image.bin
 34    timeout: 150
 35"""
 36
 37    # ============
 38    # Stage Schema
 39    # ============
 40    schema = {
 41        Optional('images'): list,
 42        Optional('timeout'): int,
 43        Optional('config_register'): str,
 44        Optional('current_running_image'): bool,
 45    }
 46
 47    # =================
 48    # Argument Defaults
 49    # =================
 50    TIMEOUT = 300
 51    CONFIG_REGISTER = '0x2102'
 52    CURRENT_RUNNING_IMAGE = False
 53
 54    # ==============================
 55    # Execution order of Stage steps
 56    # ==============================
 57    exec_order = [
 58        'delete_boot_variable',
 59        'configure_boot_variable',
 60        'set_configuration_register',
 61        'write_memory',
 62        'verify_boot_variable',
 63        'verify_configuration_register'
 64    ]
 65
 66    def delete_boot_variable(self, steps, device):
 67
 68        with steps.start("Delete any configure boot variables") as step:
 69
 70            try:
 71                device.configure("no boot system")
 72            except Exception as e:
 73                step.failed("Failed to delete configured boot variables",
 74                            from_exception=e)
 75
 76            step.passed("Successfully deleted configured boot variables")
 77
 78    def configure_boot_variable(self, steps, device, images, timeout=TIMEOUT,
 79                                current_running_image=CURRENT_RUNNING_IMAGE):
 80
 81        with steps.start("Set boot variable to images provided for {}".format(
 82                device.name)) as step:
 83
 84            if current_running_image:
 85                log.info("Retrieving and using the running image due to "
 86                         "'current_running_image: True'")
 87
 88                try:
 89                    output = device.parse('show version')
 90                    images = [output['version']['system_image']]
 91                except Exception as e:
 92                    step.failed("Failed to retrieve the running image. Cannot "
 93                                "set boot variables",
 94                                from_exception=e)
 95
 96            try:
 97                device.api.execute_set_boot_variable(
 98                    boot_images=images, timeout=timeout)
 99            except Exception as e:
100                step.failed("Failed to set boot variables to images provided",
101                            from_exception=e)
102            else:
103                step.passed("Successfully set boot variables to images provided")
104
105    def set_configuration_register(self, steps, device,
106                                   config_register=CONFIG_REGISTER, timeout=TIMEOUT):
107        with steps.start("Set config register to boot new image on {}".format(
108                device.name)) as step:
109
110            try:
111                device.api.execute_set_config_register(
112                    config_register=config_register, timeout=timeout)
113            except Exception as e:
114                step.failed("Failed to set config-register",
115                            from_exception=e)
116            else:
117                step.passed("Successfully set config register")
118
119    def write_memory(self, steps, device, timeout=TIMEOUT):
120        with steps.start("Execute 'write memory' on {}".format(device.name)) as step:
121            try:
122                device.api.execute_write_memory(timeout=timeout)
123            except Exception as e:
124                step.failed("Failed to execute 'write memory'",
125                            from_exception=e)
126            else:
127                step.passed("Successfully executed 'write memory'")
128
129    def verify_boot_variable(self, steps, device, images):
130        with steps.start("Verify next reload boot variables are correctly set "
131                         "on {}".format(device.name)) as step:
132
133            if not device.api.verify_boot_variable(boot_images=images):
134                step.failed("Boot variables are NOT correctly set")
135            else:
136                step.passed("Boot variables are correctly set")
137
138    def verify_configuration_register(self, steps, device,
139                                      config_register=CONFIG_REGISTER):
140        with steps.start("Verify config-register is as expected on {}".format(
141                device.name)) as step:
142
143            if not device.api.verify_config_register(
144                    config_register=config_register, next_reload=True):
145                step.failed("Config-register is not as expected")
146            else:
147                step.passed("Config-register is as expected")

Abstracted ChangeBootVariable (IOSXE cat9k)

Abstracted stages should redefine the three main sections (Stage Schema, Argument Defaults, and exec_order) and the docstring. This removes any doubts about what order steps run in, argument values, and changes to the schema.

In the example you can see we modified the exec_order by removing the configure_config_register and verify_config_register steps. You can also see we did not have to rewrite any steps because we are reusing the same steps from the IOSXE implementation that we inherited.

 1from genie.libs.clean.stages.iosxe.stages import ChangeBootVariable as ChangeBootVariableIosxe
 2
 3class ChangeBootVariable(ChangeBootVariableIosxe):
 4    """This stage configures boot variables of the device using the following steps:
 5
 6    - Delete existing boot variables.
 7    - Configure boot variables using the provided 'images'.
 8    - Write memory.
 9    - Verify the boot variables are as expected.
10
11Stage Schema
12------------
13change_boot_variable:
14
15    images (list): Image files to use when configuring the boot variables.
16
17    timeout (int, optional): Execute timeout in seconds. Defaults to 300.
18
19    current_running_image (bool, optional): Set the boot variable to the currently
20        running image from the show version command instead of the image provided.
21        Defaults to False.
22
23Example
24-------
25change_boot_variable:
26    images:
27        - harddisk:/image.bin
28    timeout: 150
29"""
30
31    # ============
32    # Stage Schema
33    # ============
34    schema = {
35        Optional('images'): list,
36        Optional('timeout'): int,
37        Optional('current_running_image'): bool,
38
39        # Deprecated
40        Optional('check_interval'): int,
41        Optional('max_time'): int,
42        Optional('write_memory'): bool,
43    }
44
45    # =================
46    # Argument Defaults
47    # =================
48    TIMEOUT = 300
49    CURRENT_RUNNING_IMAGE = False
50
51    # ==============================
52    # Execution order of Stage steps
53    # ==============================
54    exec_order = [
55        'delete_boot_variable',
56        'configure_boot_variable',
57        'write_memory',
58        'verify_boot_variable'
59    ]

If we needed to overwrite delete_boot_variable to change the behaviour all we do is define the method again and write our new logic.

 1from genie.libs.clean.stages.iosxe.stages import ChangeBootVariable as ChangeBootVariableIosxe
 2
 3class ChangeBootVariable(ChangeBootVariableIosxe):
 4    """This stage configures boot variables of the device using the following steps:
 5
 6    - Delete existing boot variables.
 7    - Configure boot variables using the provided 'images'.
 8    - Write memory.
 9    - Verify the boot variables are as expected.
10
11Stage Schema
12------------
13change_boot_variable:
14
15    images (list): Image files to use when configuring the boot variables.
16
17    timeout (int, optional): Execute timeout in seconds. Defaults to 300.
18
19    current_running_image (bool, optional): Set the boot variable to the currently
20        running image from the show version command instead of the image provided.
21        Defaults to False.
22
23Example
24-------
25change_boot_variable:
26    images:
27        - harddisk:/image.bin
28    timeout: 150
29"""
30
31    # ============
32    # Stage Schema
33    # ============
34    schema = {
35        Optional('images'): list,
36        Optional('timeout'): int,
37        Optional('current_running_image'): bool,
38
39        # Deprecated
40        Optional('check_interval'): int,
41        Optional('max_time'): int,
42        Optional('write_memory'): bool,
43    }
44
45    # =================
46    # Argument Defaults
47    # =================
48    TIMEOUT = 300
49    CURRENT_RUNNING_IMAGE = False
50
51    # ==============================
52    # Execution order of Stage steps
53    # ==============================
54    exec_order = [
55        'delete_boot_variable',
56        'configure_boot_variable',
57        'write_memory',
58        'verify_boot_variable'
59    ]
60
61    def delete_boot_variable(self, steps, device):
62        # Here we would write the new logic which would be
63        # ran instead of the IOSXE implementation