Stage Steps¶
Stage steps are the bread and butter of the clean stage as they do all the heavy lifting.
Lets continue with the ChangeBootVariable
example you’ve seen in the previous pages. To change the boot variable on an IOSXE device I would have to take these steps:
Delete any existing boot variables
Configure my new boot variables
Configure the config-register
Write memory
Verify the boot variables were set correctly
Verify the config-register was set correctly
You can see there are six steps to changing and verifying the boot variable on an IOSXE device.
Note
It’s important to keep the steps to a stage small and manageable. This allows us to overwrite small sections of a stage and reuse the rest (for example cat9k doesn’t use the config register).
Adding a step into the clean stage is simple. Lets go through it step-by-step for the first time.
Define a new method and give it a name that clearly describes the step. There are three arguments that will always be defined (self
, steps
, and device
) but there may be more if you are passing arguments to the stage.
1class ChangeBootVariable(BaseStage):
2
3 def delete_boot_variable(self, steps, device):
Note
We have temporarily removed the schema, argument defaults, and exec_order for these examples.
Inside the method we always start with the steps object. Create a step and give it a descriptive title as it will be shown in the logs.
1class ChangeBootVariable(BaseStage):
2
3 def delete_boot_variable(self, steps, device):
4
5 with steps.start("Delete any configure boot variables") as step:
Finally write Python code to complete any actions required.
If you need to fail the step because an exception was caught, make sure to pass the exception object to the step.failed()
call using the from_exception
argument like in the example. This will ensure the exception traceback is logged for ease of debugging.
1class ChangeBootVariable(BaseStage):
2
3 def delete_boot_variable(self, steps, device):
4
5 with steps.start("Delete any configure boot variables") as step:
6
7 try:
8 device.configure("no boot system")
9 except Exception as e:
10 step.failed("Failed to delete configured boot variables",
11 from_exception=e)
12
13 step.passed("Successfully deleted configured boot variables")
Adding back the schema, argument defaults, and exec_order, this is what you should have so far.
1from genie.metaparser.util.schemaengine import Optional
2from genie.libs.clean import BaseStage
3
4class ChangeBootVariable(BaseStage):
5
6 # ============
7 # Stage Schema
8 # ============
9 schema = {
10 Optional('images'): list,
11 Optional('timeout'): int,
12 Optional('config_register'): str,
13 Optional('current_running_image'): bool,
14 }
15
16 # =================
17 # Argument Defaults
18 # =================
19 TIMEOUT = 300
20 CONFIG_REGISTER = '0x2102'
21 CURRENT_RUNNING_IMAGE = False
22
23 # ==============================
24 # Execution order of Stage steps
25 # ==============================
26 exec_order = [
27
28 ]
29
30 def delete_boot_variable(self, steps, device):
31
32 with steps.start("Delete any configure boot variables") as step:
33
34 try:
35 device.configure("no boot system")
36 except Exception as e:
37 step.failed("Failed to delete configured boot variables",
38 from_exception=e)
39
40 step.passed("Successfully deleted configured boot variables")
Passing Step Arguments¶
To pass arguments to a clean step, add the arguments to the method and if there’s a default, set it equal to that. Here’s an example were we pass images
, timeout
, and current_running_image
. You can see we provided the defaults by using:
timeout=TIMEOUT
current_running_image=CURRENT_RUNNING_IMAGE
During runtime these values will be overwritten with user provided values (if provided). You can then use these arguments like you would in any other Python method.
1import logging
2
3from genie.metaparser.util.schemaengine import Optional
4from genie.libs.clean import BaseStage
5
6log = logging.getLogger(__name__)
7
8class ChangeBootVariable(BaseStage):
9
10 # ============
11 # Stage Schema
12 # ============
13 schema = {
14 Optional('images'): list,
15 Optional('timeout'): int,
16 Optional('config_register'): str,
17 Optional('current_running_image'): bool,
18 }
19
20 # =================
21 # Argument Defaults
22 # =================
23 TIMEOUT = 300
24 CONFIG_REGISTER = '0x2102'
25 CURRENT_RUNNING_IMAGE = False
26
27 # ==============================
28 # Execution order of Stage steps
29 # ==============================
30 exec_order = [
31
32 ]
33
34 def delete_boot_variable(self, steps, device):
35
36 with steps.start("Delete any configure boot variables") as step:
37
38 try:
39 device.configure("no boot system")
40 except Exception as e:
41 step.failed("Failed to delete configured boot variables",
42 from_exception=e)
43
44 step.passed("Successfully deleted configured boot variables")
45
46 def configure_boot_variable(self, steps, device, images, timeout=TIMEOUT,
47 current_running_image=CURRENT_RUNNING_IMAGE):
48
49 with steps.start("Set boot variable to images provided for {}".format(
50 device.name)) as step:
51
52 if current_running_image:
53 log.info("Retrieving and using the running image due to "
54 "'current_running_image: True'")
55
56 try:
57 output = device.parse('show version')
58 images = [output['version']['system_image']]
59 except Exception as e:
60 step.failed("Failed to retrieve the running image. Cannot "
61 "set boot variables",
62 from_exception=e)
63
64 try:
65 device.api.execute_set_boot_variable(
66 boot_images=images, timeout=timeout)
67 except Exception as e:
68 step.failed("Failed to set boot variables to images provided",
69 from_exception=e)
70 else:
71 step.passed("Successfully set boot variables to images provided")