Welcome to Skilletlib’s documentation!

Examples

Validation Examples

Validation Skillets work by comparing the ‘configuration objects` against a set of rules defined in each snippet. By default, the configuration is placed in the context as the ‘config’ variable and is a string representation of the raw XML. To compare and validate specific parts of the config, you can use the ‘parse’ command to convert a part of the configuration into an object that can then be used with simple logical operators or jinja filters.

Variable Parsing

This example captures a variable called update_schedule_object by converting the configuration elements found at the given xpath from the config variable. The output of this snippet is a new variable is placed into the context and is available for use in subsequent steps.

- name: create_update_schedule_object
  cmd: parse
  variable: config
  outputs:
    - name: update_schedule_object
      capture_object: /config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/update-schedule

The updated_schedule_object variable will contain all the configuration elements found at that xpath:

 <update-schedule>
  <statistics-service>
    <application-reports>yes</application-reports>
    <threat-prevention-reports>yes</threat-prevention-reports>
    <threat-prevention-pcap>yes</threat-prevention-pcap>
    <threat-prevention-information>yes</threat-prevention-information>
    <passive-dns-monitoring>yes</passive-dns-monitoring>
    <url-reports>yes</url-reports>
    <health-performance-reports>yes</health-performance-reports>
    <file-identification-reports>yes</file-identification-reports>
  </statistics-service>
  <threats>
    <recurring>
      <every-30-mins>
        <at>2</at>
        <action>download-and-install</action>
      </every-30-mins>
      <threshold>48</threshold>
    </recurring>
  </threats>
  <anti-virus>
    <recurring>
      <hourly>
        <at>4</at>
        <action>download-and-install</action>
      </hourly>
    </recurring>
  </anti-virus>
  <wildfire>
    <recurring>
      <every-min>
        <action>download-and-install</action>
      </every-min>
    </recurring>
  </wildfire>
  <global-protect-datafile>
    <recurring>
      <hourly>
        <at>40</at>
        <action>download-and-install</action>
      </hourly>
    </recurring>
  </global-protect-datafile>
  <global-protect-clientless-vpn>
    <recurring>
      <hourly>
        <at>50</at>
        <action>download-and-install</action>
      </hourly>
    </recurring>
  </global-protect-clientless-vpn>
</update-schedule>

The previous XML fragment will be converted into an object with name update_schedule_object with the following value:

{
  "update-schedule": {
    "threats": {
      "recurring": {
        "every-30-mins": {
          "at": "2",
          "action": "download-and-install"
        },
        "threshold": "48"
      }
    },
    "statistics-service": {
      "application-reports": "yes",
      "threat-prevention-reports": "yes",
      "threat-prevention-pcap": "yes",
      "threat-prevention-information": "yes",
      "passive-dns-monitoring": "yes",
      "url-reports": "yes",
      "health-performance-reports": "yes",
      "file-identification-reports": "yes"
    },
    "anti-virus": {
      "recurring": {
        "hourly": {
          "at": "4",
          "action": "download-and-install"
        }
      }
    },
    "wildfire": {
      "recurring": {
        "every-min": {
          "action": "download-and-install"
        }
      }
    },
    "global-protect-datafile": {
      "recurring": {
        "hourly": {
          "at": "40",
          "action": "download-and-install"
        }
      }
    },
    "global-protect-clientless-vpn": {
      "recurring": {
        "hourly": {
          "at": "50",
          "action": "download-and-install"
        }
      }
    }
  }
}

Validation

The validation cmd type can be used to validate configuration objects with simple logical operators and Jinja filters. This example will validate that a configuration node is present on the update_schedule_object variable.

- name: update_schedule_stats_service_configured
  when: update_schedule_object is not none
  label: Ensure Statistics Service is enabled
  test: update_schedule_object| node_present('update-schedule.statistics-service')
  documentation_link: https://docs.paloaltonetworks.com/pan-os/8-0/pan-os-new-features/content-inspection-features/telemetry-and-threat-intelligence-sharing

Note

See the Jinja Filters for details on available filters.

XML Validate

The validate_xml cmd type can be used to compare the configuration against an XML Snippet either in whole, or against a smaller portion of the XML Fragment using cherry_pick.

This will query the configuration for the XML Element at the given XPath and compare it against the contents of the ‘file’ or ‘element’ attributes. The file attribute, if found, will be rendered using Jinja and stored in the element attribute for comparison. The file or element must be rooted at the same xpath. If you have many validations to perform in the same area of the configuration, you can use cherry_pick to validate portions of a larger XML file or element.

# this example will validate that the application-reports xml fragment matches that that is found in the
# device_system.xml file
- name: validate_application_reports
  cmd: validate_xml
  xpath: /config/devices/entry[@name='localhost.localdomain']/deviceconfig/system
  file: device_system.xml
  cherry_pick: update-schedule/statistics-service/application-reports

- name: validate_statistics_service
  cmd: validate_xml
  xpath: /config/devices/entry[@name='localhost.localdomain']/deviceconfig/system
  file: device_system.xml
  cherry_pick: update-schedule/statistics-service

- name: validate_update_anti_virus
  cmd: validate_xml
  xpath: /config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/update-schedule/anti-virus
  file: anti_virus.xml

Skilletlib Skillet Types

Panos Skillet

class skilletlib.skillet.panos.PanosSkillet(metadata: dict, panoply: skilletlib.panoply.Panoply = None)
get_results() → dict

PanosSkillet will return a dict containing three keys: result, changed, and snippets. If any snippet failed, the result will be ‘failure’ otherwise ‘success’ If any successful snippet may have caused a change to the device, the ‘changed’ attribute will be ‘True’

A skillet that contains only the following snippet, will generate the output below:

- name: check_hostname_again
  cmd: op
  cmd_str: <show><system><info/></system></show>
  outputs:
    - name: url-db
      capture_pattern: ./url-db
    - name: pa-version
      capture_pattern: ./plugin_versions/entry[@name="cloud_services"]/@version
{
  'snippets': {
    'check_hostname_again': {
      'results': 'success',
      'changed': True
    }
  },
  'outputs': {
    'url-db': 'paloaltonetworks',
    'pa-version': '1.5.0'
  },
  'result': 'success',
  'changed': True
}
Returns:dict containing default outputs plus the overall result and changed flag
get_snippets() → List[skilletlib.snippet.panos.PanosSnippet]

Perform Panos Skillet specific tasks while loading each snippet

Returns:a List of PanosSnippets
initialize_context(initial_context: dict) → dict

In this panos case, we want to stash the current configuration of the panos device in question in the context, check for online mode, offline mode, or an existing panoply object

Parameters:initial_context – dict to use to initialize the context
Returns:context with additional initialized items
static load_element(snippet_def: dict, snippet_path: pathlib.Path) → dict

This method will load the snippet file found on disk into the ‘element’ attribute if the element is not already populated. This allows snippets to be ‘all-in-one’ i.e. there is no requirement for the snippets to be split into separate files. The meta-cnc.yaml file can contain all the snippets ‘inline’ in the ‘element’ attribute if desired. An example snippet def:

  • name: template xpath: /config/devices/entry[@name=’localhost.localdomain’]/template file: ../snippets/template.xml
Parameters:snippet_def – the loaded snippet definition from the skillet.yaml file. Each snippet object in the

‘snippets’ stanza is a snippet_def and is passed in here :param snippet_path: the path on the filesystem where this skillet is located. This is used to resolve relative paths for each snippet. This allows snippet file re-use across skillets. :return: snippet_def with the element populated with the resolved and loaded snippet file contents

PAN Validation Skillet

class skilletlib.skillet.pan_validation.PanValidationSkillet(metadata: dict, panoply: skilletlib.panoply.Panoply = None)
get_results() → dict

Pan-validation skillets return a dictionary with a key for each test that was executed. Each value of those keys will be a dict containing the following keys:

  • results - whether the test conditional was true or false
  • label - human readable label of the test
  • severity - a string that may be set to indicate the severity of a test
  • documentation_link - an HTTP link where the user can get more information about this test
  • output_message - A rendered output message regarding the test results
{
    "update_schedule_configured": {
        "results": true,
        "label": "Ensure Update Schedules are Configured",
        "severity": "low",
        "documentation_link": "https://iron-skillet.readthedocs.io",
        "test": "update_schedule_object is not none",
        "output_message": "Snippet Validation Passed"
    },
}
Returns:dictionary with the aforementioned keys
get_snippets() → List[skilletlib.snippet.pan_validation.PanValidationSnippet]

Perform Panos Skillet specific tasks while loading each snippet

Returns:a List of PanosSnippets

REST Skillet

class skilletlib.skillet.rest.RestSkillet(metadata: dict)
get_results() → dict

Gets the results from the REST skillet execution. This skillet does not add any additional attributes to the normal output.

The following snippet will generate the following output:

- name: Retrieve Remote Network Service IP from Prisma Access
  path: https://api.gpcloudservice.com/getAddrList/latest?fwType=gpcs_remote_network&addrType=public_ip
  operation: GET
  headers:
    header-api-key: '{{ api_key }}'
  output_type: json
  outputs:
    - name: status
      capture_pattern: $.status
    - name: fwType
      capture_pattern: $.result.fwType
    - name: addrList
      capture_pattern: $.result.addrList
{
    'snippets': {
        'Retrieve Remote Network Service IP from Prisma Access': {
            'results': 'success',
            'raw': {
                'status': 'success',
                'result': {
                    'fwType': 'gpcs_remote_network',
                    'addrListType': 'public_ip',
                    'total-count': 2,
                    'addrList': [
                        'test-XXX:x.x.x.x',
                        'pa220-test, pa220-test-2:x.x.x.x'
                    ]
                }
            }
        }
    },
    'outputs': {
        'status': 'success',
        'fwType': 'gpcs_remote_network',
        'addrList': "['test-XXX:x.x.x.x', 'pa220-test, pa220-test-2:x.x.x.x']"
    }
}
Returns:dictionary of results from the REST Skillet execute or execute_async method
get_snippets() → List[skilletlib.snippet.base.Snippet]

Loads and validates each Snippet in the REST skillet metadata file

Returns:List of Snippets for this Skillet Class

Template Skillet

class skilletlib.skillet.template.TemplateSkillet(s: dict)
get_results() → dict

TemplateSkillet will add an additional attribute into the results dict containing the value of the first snippet found to have been successfully executed

{
  "snippets": {
    "config_template": "success"
  },
  "template": "Rendered Template output"
}
Returns:dict containing default outputs plus the rendered template contained in the ‘template’ attribute
get_snippets() → List[skilletlib.snippet.template.TemplateSnippet]

Each skillet determines how it’s snippets are to be loaded and initialized. Each Skillet type must implement this method.

Returns:List of Snippets for this Skillet Class

Terraform Skillet

class skilletlib.skillet.terraform.TerraformSkillet(s: dict)
get_snippets() → List[skilletlib.snippet.base.Snippet]

Each skillet determines how it’s snippets are to be loaded and initialized. Each Skillet type must implement this method.

Returns:List of Snippets for this Skillet Class

Base Skillet

This is base class from which all Skillets derive.

class skilletlib.skillet.base.Skillet(s: dict)
dump_yaml() → str

Convert this Skillet into a YAML formatted string

Returns:YAML formatted string
execute(initial_context: dict) → dict

The heart of the Skillet class. This method executes the skillet by iterating over all the skillets returned from the ‘get_skillets’ method. Each one is checked if it should be executed if a ‘when’ conditional attribute is found, and if so, is executed using the snippet execute method.

Parameters:initial_context – context of key values pairs to use for the execution. By default this is all the

variables defined in the skillet file with their default values. Updates from user input, the environment, etc will override these default values via the ‘update_context’ method. :return: a dict containing the updated context containing the output of each of the snippets

execute_async(initial_context: dict) → Generator

Returns a generator that can be used to iterate over the output as it’s generated from each snippet. The calling application should call ‘get_results’ once the execute is complete

Parameters:initial_context – context of key values pairs to use for the execution. By default this is all the

variables defined in the skillet file with their default values. Updates from user input, the environment, etc will override these default values via the ‘update_context’ method. :return: generator[str]

get_declared_variables() → List[str]

Return a list of all variables defined in all the snippets that are not defined as an output

Returns:list of variable names
get_results() → dict

Returns the results from the skillet execution. This must be called manually if using ‘execute_async’. The returned dict will include a ‘snippets’ dictionary that contains a key for each snippet that was executed. Each snippet dictionary will contain the ‘results’ and ‘raw’ attributes.

{
  'snippets': {
    'check_hostname_again': {
      'results': 'success',
      'changed': True
    }
  },
  'outputs': {
    'url-db': 'paloaltonetworks',
    'pa-version': '1.5.0'
  },
  'result': 'success',
  'changed': True
}
Returns:dictionary of results from the Skillet execute or execute_async method
get_snippet_by_name(snippet_name: str) → skilletlib.snippet.base.Snippet

Utility method to return the snippet with snippet_name

Parameters:snippet_name – name attribute of the snippet to return
Returns:Snippet Object
get_snippets() → List[skilletlib.snippet.base.Snippet]

Each skillet determines how it’s snippets are to be loaded and initialized. Each Skillet type must implement this method.

Returns:List of Snippets for this Skillet Class
get_variable_by_name(variable_name: str) → dict

Utility method to return the variable with tne variable_name

Parameters:variable_name – name attribute of the variable to return
Returns:dictionary of variable options
initialize_context(initial_context: dict) → dict

Child classes can override this to provide any initialization information in the context. For example, ‘panos’ skillets use this to set up and initialize a Panos device object

Parameters:initial_context – Initial Context from user input, environment vars, etc
Returns:updated context with initial context items plus any initialization items
load_template(template_path: str) → str

Utility method to load a template file and return the contents as str

Parameters:template_path – relative path to the template to load
Returns:str contents
update_context(d: dict) → dict

Take the input dict d and update the skillet context. I.e. any variables passed in via environment variables will be used to update the context stored on this skillet.

Parameters:d – dictionary of key value pairs. Any keys that match ‘variable’ keys will be used to update the context
Returns:updated context stored on this skillet

Snippetlib Snippet Types

Panos Snippet

class skilletlib.snippet.panos.PanosSnippet(metadata: dict, panoply: skilletlib.panoply.Panoply)
add_filters() → None

Each snippet sub-class can add additional filters. See the PanosSnippet for examples

Returns:None
cherry_pick_element(element: str, cherry_pick_path: str) → str

Cherry picking allows the skillet builder to pull out specific bits of a larger configuration and load only the smaller chunks. This is especially useful when combined with ‘when’ conditionals

Parameters:
  • element – string containing the jinja templated xml fragment
  • cherry_pick_path – string describing the relative xpath to use to cherry pick an xml node from the element given as a parameter
Returns:

rendered and cherry_picked element

cherry_pick_xpath(base_xpath: str, cherry_picked_xpath: str) → str

When cherry picking is active, we are only going to push a smaller portion of the xml fragment. As such, we need to combine the base xpath for the xml file and the xpath to the cherry_picked node.

Ensure we can combine the base xpath and the cherry_picked xpath cleanly ideally, we end up with something like base_xpath: /config/devices/entry[@name=’localhost.localdomain’]/deviceconfig/system cherry_picked: update-schedule/statistics-service/application-reports Because the cherry_pick xpath will return the named element, we need to strip the last node from the xpath in this case, in order to push the cherry_picked element back to the device, we need to set the xpath to /config/devices/entry[@name=’localhost.localdomain’]/deviceconfig/system/update-schedule/statistics-service

Parameters:
  • base_xpath – base xpath for the xml fragment
  • cherry_picked_xpath – relative xpath for cherry picking a portion of the xml fragment
Returns:

combined and rendered xpath

static compare_element_at_xpath(config: str, element: str, xpath: str, context: dict) → bool

Grab an xml fragment from the config given at xpath and compare it to this element

Parameters:
  • config – XML document string from which to pull the XML element to compare
  • element – element to check against
  • xpath – xpath to grab an xml fragment from the config for comparison
  • context – jinja context used to interpolate any variables that may be present in the template
Returns:

bool true if they match

execute(context: dict) → Tuple[str, str]

Execute this Snippet and return a tuple consisting on raw output and a string representing success, failure, or running.

Each snippet sub class must override this method!

Parameters:context – context to use for variable interpolation
Returns:Tuple containing raw snippet output and string indicated success or failure
get_default_output(results: str, status: str) → dict

Override the default snippet get_default_output to not include raw results

Parameters:
  • results – raw output from snippet execution
  • status – status of the snippet.execute method
Returns:

dict of default outputs

render_metadata(context: dict) → dict

Renders each item in the metadata using the provided context. Currently renders the xpath and element for PANOS type skillets

Parameters:context – dict containing key value pairs to
Returns:dict containing the snippet definition metadata with the attribute values rendered accordingly
sanitize_metadata(metadata: dict) → dict

Ensure all required keys are present in the snippet definition

Parameters:metadata – dict
Returns:dict

PAN Validation Snippet

class skilletlib.snippet.pan_validation.PanValidationSnippet(metadata: dict, panoply: skilletlib.panoply.Panoply)

Pan validation Snippet

execute(context: dict) → Tuple[str, str]

Execute method in pan_validation snippet overrides the execute method in panos to add ensure any exception caught always results in a failed test

Parameters:context – snippet context used for tests
Returns:tuple consisting of results, (success | failure)
handle_output_type_validation(results: str) → dict

Handle output type validation results

Parameters:results – results from the test execution
Returns:dict containing validation messages

REST Snippet

class skilletlib.snippet.rest.RestSnippet(payload_str: str, metadata: dict, session: requests.sessions.Session)

Rest Snippet

execute(raw_context: dict) → Tuple[str, str]

Execute this Snippet and return a tuple consisting on raw output and a string representing success, failure, or running.

Each snippet sub class must override this method!

Parameters:context – context to use for variable interpolation
Returns:Tuple containing raw snippet output and string indicated success or failure
sanitize_metadata(metadata: dict) → dict

Clean and sanitize metadata elements in this snippet definition

Parameters:metadata – dict
Returns:dict

Template Snippet

class skilletlib.snippet.template.TemplateSnippet(template_str, metadata)

TemplateSnippet implements a basic template object snippet

execute(context: dict) → Tuple[str, str]

Execute this Snippet and return a tuple consisting on raw output and a string representing success, failure, or running.

Each snippet sub class must override this method!

Parameters:context – context to use for variable interpolation
Returns:Tuple containing raw snippet output and string indicated success or failure

Base Snippet

This is base class from which all Snippets derive.

class skilletlib.snippet.base.Snippet(metadata: dict)

BaseSnippet implements a basic Noop snippet

add_filters() → None

Each snippet sub-class can add additional filters. See the PanosSnippet for examples

Returns:None
capture_outputs(results: str, status: str) → dict

All snippet output or portions of snippet output can be captured and saved on the context as a new variable

Parameters:
  • results – the raw output from the snippet execution
  • status – status of the snippet.execute method
Returns:

a dictionary containing all captured variables

execute(context: dict) → Tuple[str, str]

Execute this Snippet and return a tuple consisting on raw output and a string representing success, failure, or running.

Each snippet sub class must override this method!

Parameters:context – context to use for variable interpolation
Returns:Tuple containing raw snippet output and string indicated success or failure
execute_conditional(test: str, context: dict) → bool

Evaluate ‘test’ conditionals and return a bool

Parameters:
  • test – string of the conditional to execute
  • context – jinja context containing previous outputs and user supplied variables
Returns:

boolean

get_default_output(results: str, status: str) → dict

each snippet type can override this method to provide it’s own default output. This is used when there are no variables defined to be captured

Parameters:
  • results – raw output from snippet execution
  • status – status of the snippet.execute method
Returns:

dict of default outputs

get_loop_parameter() → list

Returns the loop parameter for this snippet. If a loop parameter is not defined in the snippet def, this returns a list with a single blank str. Otherwise, return the value of the loop parameter as a list.

Returns:value of loop_parameter from the context or a list with a single blank str
get_output() → Tuple[str, str]

get_output can be used when a snippet executes async and cannot or will not return output right away snippets that operate async must override this method

Returns:Tuple containing the skillet output as a str and a str indicating success of failure
get_output_variables() → list

Returns a list of all output variables. This is used to determine if a snippet variable should be considered undeclared.

Returns:list of str representing output variable names
get_snippet_variables() → list

Returns a list of variables defined in this snippet that are NOT defined as outputs

Returns:list of str representing variables found in the jinja templates
get_variables_from_template(template_str: str) → list

Returns a list of jinja2 variable found in the template

Parameters:template_str – jinja2 template
Returns:list of variables declared in the template
is_filtered(context) → bool

Determines if a snippet should be available for execution based on the presence of the __filter_snippets object in the context. Snippets can be filtered by the following:

include_by_name: list of names to check. Only snippet names included in this list will be executed include_by_tag: list of tags to check. Only snippets with those tags will be executed include_by_regex: regular expression match. Only snippets whose name matches the regex will be executed

any snippet that does not match any of the above rules will be filtered out. The rules are inclusive OR

Parameters:context – Snippet Context
Returns:bool
render(template_str: str, context: (<class 'dict'>, None)) → str

Convenience method to quickly render a template_str using the provided context

Parameters:
  • template_str – jinja2 template to render
  • context – context to pass to the jinja2 environment
Returns:

rendered string

render_metadata(context: dict) → dict

Each snippet sub class can override this method to perform jinja variable interpolation on various items in it’s snippet definition. For example, the PanosSnippet will check the ‘xpath’ attribute and perform the required interpolation.

This handles regular strings, lists, and dictionaries such as:

in snippet class template_metadata = {‘render_me’, ‘render_all’, ‘render_list’}

in metadata snippets:

  • name: render_me_snippet render_me: render_{{ this }}

  • name: render_all_snippet render_all:

    a_key: some_{{ value }} another_key: some_other_{{ value }}

  • name: render_list_snippet render_list:

    • here_is_a_{{ value }}
    • another_{{ value }}
Parameters:context – context from environment
Returns:metadata with jinja rendered variables
reset_metadata()

Reset the metadata to the original metadata. This is used during looping so we can render items in the metadata on each iteration.

Returns:None
sanitize_metadata(metadata: dict) → dict

method to sanitize metadata. Each snippet type can override this provide extra logic over and above just checking the required and optional fields

Parameters:metadata – snippet metadata
Returns:sanitized snippet metadata
should_execute(context: dict) → bool

Evaluate ‘when’ conditionals and return a bool if this snippet should be executed

Parameters:context – jinja context containing previous outputs and user supplied variables
Returns:boolean
update_context(context: dict) → dict

This will update the snippet context with the passed in dict. This gets called inside of ‘should_execute’

Parameters:context – dict of the outer context
Returns:newly updated context

SkilletLoader

class skilletlib.SkilletLoader(path=None)

SkilletLoader is used to find and load Skillets from their metadata files, either from the local filesystem or from a git repository URL

Parameters:path – local relative path to search for all Skillet meta-data files
compile_skillet_dict(skillet: dict) → dict

Compile the skillet dictionary including any included snippets from other skillets. Included snippets and variables will be inserted into the skillet dictionary and any replacements / updates to those snippets / variables will be made before hand.

Parameters:skillet – skillet definition dictionary
Returns:full compiled skillet definition dictionary
create_skillet(skillet_dict: dict) → skilletlib.skillet.base.Skillet

Creates a Skillet object from the given skillet definition

Parameters:skillet_dict – Dictionary loaded from the skillet.yaml definition file
Returns:Skillet Object
static debug_skillet_structure(skillet: dict) → list

Verifies the structure of a skillet and returns a list of errors or warning if found

Parameters:skillet – Skillet Definition Dictionary
Returns:list of errors or warnings if found
get_skillet_with_name(skillet_name: str, include_resolved_skillets=False) -> (<class 'skilletlib.skillet.base.Skillet'>, None)

Returns a single skillet from the loaded skillets list that has the matching ‘name’ attribute

Parameters:
  • skillet_name – Name of the skillet to return
  • include_resolved_skillets – boolean of whether to also check the resolved skillet list
Returns:

Skillet or None

load_all_label_values(label_name: str) → list

Returns a list of label values defined across all snippets with a given label for example:

labels:
label_name: label_value

will add ‘label_value’ to the list

Parameters:label_name – name of the label to search for
Returns:list of strings representing all found label values for given key
load_all_skillets_from_dir(directory: (<class 'str'>, <class 'pathlib.Path'>)) → List[skilletlib.skillet.base.Skillet]

Recursively iterate through all sub-directories and locate all found skillets Returns a list of Loaded Skillets

Parameters:directory – parent directory in which to start iterating
Returns:list of Skillet objects
load_from_git(repo_url, repo_name, repo_branch, local_dir=None) → List[skilletlib.skillet.base.Skillet]

Performs a local clone of the given Git repository URL and returns a list of all found skillets defined therein.

Parameters:
  • repo_url – Repository URL
  • repo_name – name given to the repository
  • repo_branch – branch to checkout
  • local_dir – local directory where to clone the git repository into
Returns:

List of Skillets

load_skillet(skillet_path: str) → skilletlib.skillet.base.Skillet

Returns a Skillet object from the given path

Parameters:skillet_path – full path to the skillet YAML file
Returns:Skillet object
load_skillet_dict_from_path(skillet_path: (<class 'str'>, <class 'pathlib.Path'>)) → dict

Loads the skillet metadata file into a skillet_dict dictionary

Parameters:skillet_path – path in which to look for a metadata file
Returns:skillet dictionary
load_skillet_dicts_from_git(repo_url, repo_name, repo_branch, local_dir=None) → List[dict]

Performs a local clone of the given Git repository URL and returns a list of all found skillet definition dictionaries defined therein.

Parameters:
  • repo_url – Repository URL
  • repo_name – name given to the repository
  • repo_branch – branch to checkout
  • local_dir – local directory where to clone the git repository into
Returns:

List of Skillets

load_skillet_from_path(skillet_path: (<class 'str'>, <class 'pathlib.Path'>)) → skilletlib.skillet.base.Skillet

Returns a Skillet object from the given path.

Parameters:skillet_path – path in which to search for a skillet
Returns:Skillet object of the correct type
load_skillets_from_git(repo_url, repo_name, repo_branch, local_dir=None) → List[skilletlib.skillet.base.Skillet]

Performs a local clone of the given Git repository URL and returns a list of all found skillets defined therein.

Parameters:
  • repo_url – Repository URL
  • repo_name – name given to the repository
  • repo_branch – branch to checkout
  • local_dir – local directory where to clone the git repository into
Returns:

List of Skillets

static normalize_skillet_dict(skillet: dict) → dict

Attempt to resolve common configuration file format errors

Parameters:skillet – a loaded skillet/snippet
Returns:skillet/snippet that has been ‘fixed’
resolved_skillets

alias of typing.List

skillets

alias of typing.List

Panoply

class skilletlib.panoply.EphemeralPanos(hostname: Optional[str] = None, api_username: Optional[str] = None, api_password: Optional[str] = None, api_port: Optional[int] = 443, serial_number: Optional[str] = None, debug: Optional[bool] = False, api_key: Optional[str] = None)

EphemeralPanos is used when a Panos instance may or may not be online and available at all times. This is useful when instantiating instances via a CI/CD pipeline or other automation tool.

class skilletlib.panoply.Panoply(hostname: Optional[str] = None, api_username: Optional[str] = None, api_password: Optional[str] = None, api_port: Optional[int] = 443, serial_number: Optional[str] = None, debug: Optional[bool] = False, api_key: Optional[str] = None)

Panoply is a wrapper around pan-python PanXAPI class to provide additional, commonly used functions

ad_hoc(qs: str) → str

Runs an ad_hoc command against this device.

example: type=op&action=complete&xpath=/operations/show/config/saved

Parameters:qs – qs
Returns:the unparsed xml document from the device API
backup_config()

Saves a named backup on the PAN-OS device. The format for the backup is ‘config-backup-20190424000000.xml’

Returns:xml results from the op command sequence
check_content_updates(content_type: str) -> (<class 'str'>, None)

Iterate through all available content of the specified type, locate and return the version with the highest version number. If that version is already installed, return None as no further action is necessary

Parameters:content_type – type of content to check
Returns:version-number to download and install or None if already at the latest
commit(force_sync=True) → str

Perform a commit operation on this device instance -

Raises:PanoplyException – if commit failed
Parameters:force_sync – Flag to enable sync commit or async
Returns:String from the API indicating success or failure
commit_gpcs(force_sync=True) → str

Perform a commit operation on this device instance specifically for gpcs remote networks Note - you must do a full commit to panorama before you invoke this commit!

Raises:PanoplyException – if commit failed
Parameters:force_sync – Flag to enable sync commit or async
Returns:String from the API indicating success or failure
connect(allow_offline: Optional[bool] = False) → None

Attempt to connect to this device instance

Parameters:allow_offline – Do not raise an exception if this device is offline unless there is an authentication

error :return: None

deactivate_vm_license(api_key: str = None) → bool

Deactivate VM-Series Licenses. Will set the API Key is not already set.

Parameters:api_key – Optional api_key.
Returns:boolean True on success / False otherwise
execute_cli(cmd_str: str) → str

Short-cut to execute a simple CLI op cmd

Parameters:cmd_str – CLI command to send to the NGFW
Returns:raw output from the command
execute_cmd(cmd: str, params: dict, context=None) → str

Execute the given cmd using the xapi.

Parameters:
  • cmd – Valid options are: ‘op’, ‘show’, ‘get’, ‘delete’, ‘set’, ‘edit’, ‘override’, ‘move’, ‘rename’, ‘clone’, ‘validate’
  • params – valid parameters for the given cmd type
  • context – skillet context
Returns:

raw results from the cmd output, raises SkilletLoaderException

execute_op(cmd_str: str, cmd_xml=False, parse_result=True) → str

Executes an ‘op’ command on the NGFW

Parameters:
  • cmd_str – op command to send
  • cmd_xml – Flag to determine if op command requires XML encoding
  • parse_result – Optional flag to indicate whether to return parsed xml results (xml_result) from xapi - not

all commands return valid XML. Setting this to ‘false’ will return the raw string from the API. :return: raw output from the device

fetch_license(auth_code: str, force_fetch_license: bool = False) → bool

Fetch and install licenses for PAN-OS NGFW

Parameters:
  • auth_code – Authorization code to use to license the NGFW
  • force_fetch_license – Fetch licenses even if NGFW is already licensed
Returns:

True when license installation succeeds / False otherwise

filter_connected_devices(filter_terms=None) → list

Returns the list of connected devices filtered according to the given terms.

Filter terms are based on keys in the device facts. Matches are done using simple regex match

Parameters:filter_terms – dict containing key value pairs. Keys match keys from the return device facts and values

are regex expressions used to match those keys :return: list of devices that match ALL filter terms

generate_baseline(reset_hostname=False) → str

Load baseline config that contains ONLY connecting username / password use device facts to determine which baseline template to load see template/panos/baseline_80.xml for example

Parameters:
  • self
  • reset_hostname – Flag to reset hostname back to a default value (baseline in this case)
Returns:

string contents of baseline config

generate_set_cli_from_configs(previous_config: str, latest_config: str) → list

Takes two configuration files, converts them to set commands, then returns only the commands found in the ‘latest_config’ vs the ‘previous_config’. This allows the user to quickly configure one firewall, generate a ‘set cli’ diff and move those configs to another firewall

Parameters:
  • previous_config – Starting config
  • latest_config – Ending config
Returns:

list of set cli commands required to convert previous to latest

generate_skillet(from_candidate=False) → list

Generates a skillet from the changes detected on this device. This will attempt to create the xml and xpaths for everything that is found to have changed

Parameters:from_candidate – If your changes on in the candidate config, this will detect changes between the running

config and the candidate config. If False, this will detect changes between the running config and a generic baseline configuration :return: list of xpaths

generate_skillet_from_configs(previous_config: str, latest_config: str) → list

Generates a skillet from the diffs between two XML configuration files

Parameters:
  • previous_config – Configuration backup taken before desired changes are made to the device
  • latest_config – Configuration backup taken after desired changes are made
Returns:

list of dictionaries that contain the following keys: * name * element * xpath * full_xpath

get_configuration(config_source='running') → str

Get the configuration from the device.

Parameters:config_source – Configuration source. This can be either ‘candidate, running, baseline, or an

audit version number. Candidate is the candidate config, running is the running config, baseline is an autogenerated bare configuration, and version number is the previously saved running configuration. A positive version number will get the configuration version matching that id. A negative version number will get the most recent to least recent configuration versions where (-1) is the most recent previous running config. :return: configuration xml as a string or a blank string if not connected

get_configuration_version(version: str) → str

Returns a configuration version on the device. Use ‘list_configuration_versions’ to get a list of available options

Parameters:version – version number of the configuration version to export
Returns:configuration as an XML encoded string
get_extended_facts() → dict

Get extended facts about the device to which we are connected.

Return interfaces, security_rules, and zones

Returns:dict with extended facts
get_facts() → dict

Gather system info and keep on self.facts This gets called on every connect

Returns:dict containing all system facts
get_interfaces() → dict

Return a dict of all interfaces configured on this device. The dict has the following structure:

{
“ifnet”: {
“entry”: [
{
“name”: “ethernet1/1”, “zone”: “internet”, “fwd”: “vr:default”, “vsys”: “1”, “dyn-addr”: null, “addr6”: null, “tag”: “0”, “ip”: “10.48.58.161/23”, “id”: “16”, “addr”: null

}

]

}, “hw”: {

“entry”: [
{
“name”: “ethernet1/1”, “duplex”: “full”, “type”: “0”, “state”: “up”, “st”: “10000/full/up”, “mac”: “fa:16:3e:65:7d:05”, “mode”: “(autoneg)”, “speed”: “10000”, “id”: “16”

}

]

}

}

Returns:dict with two keys ‘ifnet’ and ‘hw’.
static get_ordered_xpaths() → tuple

Returns a list of ordered xpaths for use in ordering snippets and set commands

Will be enhanced one day with version and model specific information if necessary

Returns:tuple of two lists, xpaths and post_xpaths
get_saved_configuration(configuration_name: str) → str

Returns a saved configuration on the device. Use ‘list_saved_configuration’ to get a list of available options

Parameters:configuration_name – name of the saved configuration to export
Returns:configuration as an XML encoded string
get_security_rules() → list

Return the list of configured security rules on this device.

  • returns a blank list for Panorama devices!
Returns:list of security rules
get_zones() → list

Return the list of configured zones on this device.

  • returns a blank list for Panorama devices!
Returns:list of zone names
has_running_jobs() → bool

Simple check to determine if there are any running jobs on this device

Returns:bool True if there is currently a running job
import_file(filename: str, file_contents: (<class 'str'>, <class 'bytes'>), category: str) → bool

Import the given file into this device

Parameters:
  • filename
  • file_contents
  • category – ‘configuration’
Returns:

bool True on success

list_saved_configurations() → list

Returns a list of saved configuration files on this device

Returns:list of saved configurations
load_baseline() → bool

Load baseline config that contains ONLY connecting username / password use device facts to determine which baseline template to load see template/panos/baseline_80.xml for example

Parameters:self
Returns:bool true on success
load_config(filename: str) → bool

Loads the named configuration file into this device

Parameters:filename – name of the configuration file on the device to load. Note this filename must already exist

on the target device :return: bool True on success

static sanitize_element(element: str) → str

Eliminate some unneeded characters from the XML snippet if they appear.

Parameters:element – element str
Returns:sanitized element str
set_at_path(name: str, xpath: str, xml_str: str) → None

Insert XML into the configuration tree at the specified xpath

Parameters:
  • name – name of the snippet - used in logging and debugging only
  • xpath – full xpath where the xml element will be inserted
  • xml_str – string representation of the XML element to insert
Returns:

None

set_license_api_key(api_key: str) → bool

Set’s the Palo Alto Networks Support API Key in the firewall. This is required to deactivate a VM-Series NGFW.

Parameters:api_key – Your support account API Key found on the support.paloaltonetworks.com site
Returns:boolean True on success / False otherwise
update_dynamic_content(content_type: str) → bool

Check for newer dynamic content and install if found

Parameters:content_type – type of content to check. can be either: ‘content’, ‘anti-virus’, ‘wildfire’
Returns:bool True on success
wait_for_device_ready(interval=30, timeout=600) → bool

Loop and wait until device is ready or times out

Parameters:
  • interval – how often to check in seconds
  • timeout – how long to wait until we declare a timeout condition
Returns:

boolean true on ready, false on timeout

wait_for_job(job_id: str, interval=10, timeout=600) → bool

Loops until a given job id is completed. Will timeout after the timeout period if the device is offline or otherwise unavailable.

Parameters:
  • job_id – id the job to check and wait for
  • interval – how long to wait between checks
  • timeout – how long to wait with no response before we give up
Returns:

bool true on content updated, false otherwise

class skilletlib.panoply.Panos(hostname: Optional[str], api_username: Optional[str], api_password: Optional[str], api_port: Optional[int] = 443, serial_number: Optional[str] = None, debug: Optional[bool] = False, api_key: Optional[str] = None)

Panos is used to connect to PAN-OS devices that are expected to be currently and always online! Exceptions will be raised in any event that we cannot connect!

Outputs

Executing Skillets

from skilletlib import SkilletLoader
sl = SkilletLoader()
skillet = sl.load_skillet('./ssl_decrypt_settings.skillet.yaml')
context = dict()
context['internal_zone'] = 'inside'
context['external_zone'] = 'outside'
out = skillet.execute(context)
print(out)

Examining the Output

Each Skillet type may return data differently. For example, pan_validation Skillets will return a dict with each key being the name of a snippet that was executed. It’s value will be the results of that snippet.

{
    "update_schedule_configured": {
        "results": true,
        "label": "Ensure Update Schedules are Configured",
        "severity": "low",
        "documentation_link": "https://iron-skillet.readthedocs.io",
        "test": "update_schedule_object is not none",
        "output_message": "Snippet Validation Passed"
    },
}

Outputs Per Type

View the documentation for the ‘get_results’ method of each skillet class to determine what structure is returned by the Skillet type.

Output Templates

A very common use case is to collect some information from a NGFW, filter or otherwise manipulate that data, then display it to the user. This is so common, we’ve added a simple way to do both of these tasks in the same Skillet.

Output templates are normal jinja2 templates used to display data after the Skillet exeuciton is complete. By adding an ‘output_template’ key with a value of a relative path to a jinja2 template file, skilletlib will find and load that template, then render it using the outputs and context of the Skillet.

By default the template engine has access to the following context items:
  • snippet_outputs
  • captured_outputs
  • context

See the ‘output_template’ directory in example_skillets for a complete example.

Working with Objects

To make working with objects easier, we have created the following jinja filters:

  • tag_present
  • tag_absent
  • attribute_present
  • attribute_absent
  • element_value
  • element_value_contains
  • append_uuid
  • md5_hash

Jinja Filters

tag_present

This filter will validate that the given path is present in the variable object. The path argument is a ‘.’ or ‘/’ separated list. The variable object is inspected to verify each item in the path list is present. If all elements are found, this filter will return True. This is useful to validate a specific element is present.

- name: update_schedule_stats_service_configured
  label: Ensure Statistics Service is enabled
  test: update_schedule_object| tag_present('update-schedule.statistics-service')

element_value

This filter will return the value of the given path in the variable object. This value can then be used with any valid jinja expression. The path argument is a ‘.’ or ‘/’ separated list. The variable object is inspected to verify each item in the path list is present. If all elements are found, this filter will return leaf node text value.

- name: ensure_ip_address
  label: Ensure IP Address is configured
  test: device_system | element_value('ip-address') == '10.10.10.10'

attribute_present

This filter will determine if a node exists with the given attribute name and value. This is useful for parts of the configuration where there may be many ‘entries’ under a specific xpath. For example, security profiles or interfaces. This filter takes a configuration path as the first argument, similar to the tag_present filter. The second argument is the attribute name and the third is the attribute value. If the configuration path is found, and has an attribute that matches both the name and value, this filter will return True.

- name: check_profile_exists
  when: network_profiles is not none
  label: Ensure Named profile exists
  test: network_profiles | attribute_present('entry', 'name', 'default')

element_value_contains

This filter is useful for times when the xpath may contain a list of items. The path argument is a ‘.’ or ‘/’ separated list. The variable object is inspected to verify each item in the path list is present. If all elements are found, this filter will inspect the found value. If the value is a list, this filter will determine if the second argument is present in the list. If the value is a string, the filter will check if the string matches the second argument.

- name: security_rules_outbound_edl
  label: check for IronSkillet outbound EDL block rule member
  test: security_rule_outbound_edl | element_value_contains('destination.member', 'panw-bulletproof-ip-list')
  documentation_link: https://ironscotch.readthedocs.io/en/docs_dev/viz_guide_panos.html#device-setup-telemetry-telemetry

items_present

This filter will iterate over a list and ensure all items from the first list appear in the second list. The second list can be a list of objects, in which case an optional path argument may be supplied.

Consider the case where you have a list of objects. Each object has it’s own list of members. You want to ensure that all items from a list appear at least once in one of the objects member lists. For example, ensure a list of blocked application appear in at least one block rule.

snippets:
  - name: grab_security_rules
    cmd: parse
    variable: config
    outputs:
      - name: security_rules
        capture_list: /config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/rulebase/security/rules/entry
      - name: deny_rules
        capture_expression: security_rules
        filter_items: item | element_value('entry.action') == 'deny'
  - name: all_apps_blocked
    label: Ensure all blocked apps appear in the rules
    test: blocked_apps | items_present(deny_rules, 'entry.application.member')
    documentation_link: https://iron-skillet.readthedocs.io

Additional Filters

Additional filters have been included from the Jinja2 Ansible Filters project.

Included filters

  • b64decode
  • b64encode
  • basename
  • bool
  • checksum
  • comment
  • dirname
  • expanduser
  • expandvars
  • extract
  • fileglob
  • flatten
  • from_json
  • from_yaml
  • from_yaml_all
  • ans_groupby
  • hash
  • mandatory
  • md5
  • quote
  • ans_random
  • random_mac
  • realpath
  • regex_escape
  • regex_findall
  • regex_replace
  • regex_search
  • relpath
  • sha1
  • shuffle
  • splitext
  • strftime
  • subelements
  • ternary
  • to_datetime
  • to_json
  • to_nice_json
  • to_nice_yaml
  • to_uuid
  • to_yaml
  • type_debug
  • win_basename
  • win_dirname
  • win_splitdrive

About

Skilletlib is a library for working with Skillets. Skillets are a collection of configuration templates and some metadata about how those templates should be rendered and applied. Skillets were created to help manage complex shareable configuration sets for PAN-OS devices.

Skillets can also be thought of as wrappers around atomic automation units. A collection of PAN-OS XML configuration snippets can be grouped together as a unit to be shared and applied together. Skilletlib provides a convenient mechanism to examine and apply these automation units.

Building Skillets

This documentation is intended document the internals the Skilletlib python library. For more information about building and using Configuration and Validation Skillets, refer to the Skillet Builder documentation.

Disclaimer

This software is provided without support, warranty, or guarantee. Use at your own risk.

Indices and tables