ansible-collections / ansible.network

Ansible Network Collection for network and IP utilities that are not specific to any platform or OS.
GNU General Public License v3.0
18 stars 18 forks source link

[proposal] Plugin to validate network configuration against a predefined rule set #15

Closed NilashishC closed 3 months ago

NilashishC commented 3 years ago

Proposal:

Add support for a config validation plugin that evaluates network configurations against a predefined set of rules and renders warnings/failures based on the outcome of the validation task. This is aimed to precede *_config tasks in a playbook and would allow us to catch potential errors in the candidate config before the succeeding tasks make configuration changes on the target. The rule set(s) are expected to be implemented by the users based on their exact needs and target platforms.

Motivation

Describe the reasons for this proposal. Have the ability to determine the "correctness" of the configuration to be pushed and ensure a more predictable outcome from the *_config modules.

Problems

As a user of the *_config modules:

Solution proposal

Playbook:

- hosts: sw01
  gather_facts: no
  tasks:
  - name: Validate candidate config against a pre-defined set of rules
    ansible.utils.validate_config:
      config: "{{ lookup('file', 'candidate.cfg') }}"
      rules: "{{ role_path }}/rules/rules.yaml"

candidate.cfg:

interface Eth1/1
  description test-description-too-long
  no switchport

interface Ethernet1/2
  description intf-2

interface port-channel1
  description po-1

interface po2.1
  description po2

interface Loopback 10
  description lo10

rules.yaml:

---
- name: 1. Interface description should not be more than 8 chars
  example: "Matches description this-is-a-long-description"
  rule: 'description\s(.{9,})'
  action: warn

- name: Ethernet interface names should be in format Ethernet[Slot/chassis number].[sub-intf number (optional)]
  example: "Matches interface Eth1/1, interface Eth 1/1, interface Ethernet 1/1, interface Ethernet 1/1.100"
  rule: 'interface\sE(?!\w{7}\d/\d(.\d+)?)'
  action: fail

- name: Ethernet interface names should be in format Ethernet[Slot/chassis number].[sub-intf number (optional)]
  example: "Matches interface eth1/1, interface eth 1/1, interface ethernet 1/1, interface ethernet 1/1.100"
  rule: 'interface\se(?!\w{7}\d/\d(.\d+)?)'
  action: fail

- name: Loopback interface names should be in format loopback[Virtual Interface Number]
  example: "Matches interface Lo10, interface Loopback 10"
  rule: 'interface\sl(?!\w{7}\d)'
  action: fail

- name: Loopback interface names should be in format loopback[Virtual Interface Number]
  example: "Matches interface lo10, interface loopback 10"
  rule: 'interface\sL(?!\w{7}\d)'
  action: fail

- name: Port Channel names should be in format port-channel[Port Channel number].[sub-intf number (optional)]
  example: "Matches interface port-channel 10, interface po10, interface port-channel 10.1"
  rule: 'interface\sp(?!\w{3}-\w{7}\d(.\d+)?)'
  action: fail

- name: Port Channel names should be in format port-channel[Port Channel number].[sub-intf number (optional)]
  example: "Matches interface Port-channel 10, interface Po10, interface Port-channel 10.1"
  rule: 'interface\sP(?!\w{3}-\w{7}\d(.\d+)?)'
  action: fail

Result:

result: {
  failures: [
    {
        "name": Ethernet interface names should be in format Ethernet[Slot/chassis number].[sub-intf number (optional)],
        "rule": 'interface\sE(?!\w{7}\d/\d(.\d+)?)',
        "config_line": "interface Eth1/1",
    },
    {
        "name": Port Channel names should be in format port-channel[Port Channel number].[sub-intf number (optional)],
        "rule": 'interface\sp(?!\w{3}-\w{7}\d(.\d+)?)',
        "config_line": "interface po2.1",
    },
    {
        "name": Loopback interface names should be in format loopback[Virtual Interface Number],
        "rule": 'interface\sL(?!\w{7}\d)',
        "config_line": "interface Loopback 10",
    },
  ],
  warnings: [
    {
        "name": "Interface description should not be more than 8 chars",
        "rule": "description\s(.{9,})",
        "config_line": "description test-description-too-long", 
    },
  ]
}
ganeshrn commented 3 years ago

I think it should implement as a sub-plugin of validate plugin For example:

- hosts: sw01
  gather_facts: no
  tasks:
  - name: Validate candidate config against a pre-defined set of rules
    ansible.utils.validate:
      data: "{{ lookup('file', 'candidate.cfg') }}"
      engine: "ansible.netcommon.native_validator".   # name subject to bikeshedding :-)
      criteria:
      - "{{ role_path }}/rules/rules.yaml"