diff --git a/changelogs/fragments/t6721-vyos_fw_global.yml b/changelogs/fragments/t6721-vyos_fw_global.yml new file mode 100644 index 00000000..bbd86f60 --- /dev/null +++ b/changelogs/fragments/t6721-vyos_fw_global.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - vyos_firewall_global - Added 'diff' support diff --git a/changelogs/fragments/t6823_ipv6_autoconf.yml b/changelogs/fragments/t6823_ipv6_autoconf.yml new file mode 100644 index 00000000..8dfa04ee --- /dev/null +++ b/changelogs/fragments/t6823_ipv6_autoconf.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - vyos_l3_interfaces - ipv6 auto-config option incorrectly set diff --git a/changelogs/fragments/t7933_fw_rules_offload.yml b/changelogs/fragments/t7933_fw_rules_offload.yml new file mode 100644 index 00000000..4f44c497 --- /dev/null +++ b/changelogs/fragments/t7933_fw_rules_offload.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - vyos_firewall_rules - add 'offload' action diff --git a/docs/vyos.vyos.vyos_firewall_rules_module.rst b/docs/vyos.vyos.vyos_firewall_rules_module.rst index 982dd1d4..051a9791 100644 --- a/docs/vyos.vyos.vyos_firewall_rules_module.rst +++ b/docs/vyos.vyos.vyos_firewall_rules_module.rst @@ -1,2941 +1,2960 @@ .. _vyos.vyos.vyos_firewall_rules_module: ***************************** vyos.vyos.vyos_firewall_rules ***************************** **Firewall rules resource module** Version added: 1.0.0 .. contents:: :local: :depth: 1 Synopsis -------- - This module manages firewall rule-set attributes on VyOS devices Parameters ---------- .. raw:: html + + + + + + + +
Parameter Choices/Defaults Comments
config
list / elements=dictionary
A dictionary of Firewall rule-set options.
afi
string / required
    Choices:
  • ipv4
  • ipv6
Specifies the type of rule-set.
rule_sets
list / elements=dictionary
The Firewall rule-set list.
default_action
string
    Choices:
  • drop
  • reject
  • accept
  • jump
Default action for rule-set.
drop (Drop if no prior rules are hit (default))
reject (Drop and notify source if no prior rules are hit)
accept (Accept if no prior rules are hit)
jump (Jump to another rule-set, 1.4+)
default_jump_target
string
Default jump target if the default action is jump.
Only valid in 1.4 and later.
Only valid when default_action = jump.
description
string
Rule set description.
enable_default_log
boolean
    Choices:
  • no
  • yes
Option to log packets hitting default-action.
filter
string
    Choices:
  • input
  • output
  • forward
Filter type (exclusive to "name").
Supported in 1.4 and later.
name
string
Firewall rule set name.
Required for 1.3- and optional for 1.4+.
rules
list / elements=dictionary
A dictionary that specifies the rule-set configurations.
action
string
    Choices:
  • drop
  • reject
  • accept
  • inspect
  • continue
  • return
  • jump
  • +
  • offload
  • queue
  • synproxy
Specifying the action.
inspect is available < 1.4
continue, return, jump, queue, synproxy are available >= 1.4
description
string
Description of this rule.
destination
dictionary
Specifying the destination parameters.
address
string
Destination ip address subnet or range.
IPv4/6 address, subnet or range to match.
Match everything except the specified address, subnet or range.
Destination ip address subnet or range.
group
dictionary
Destination group.
address_group
string
Group of addresses.
network_group
string
Group of networks.
port_group
string
Group of ports.
port
string
Multiple destination ports can be specified as a comma-separated list.
The whole list can also be "negated" using '!'.
For example:'!22,telnet,http,123,1001-1005'.
disable
boolean
    Choices:
  • no
  • yes
Option to disable firewall rule.
aliased to disabled

aliases: disabled
fragment
string
    Choices:
  • match-frag
  • match-non-frag
IP fragment match.
icmp
dictionary
ICMP type and code information.
code
integer
ICMP code.
type
integer
ICMP type.
type_name
string
    Choices:
  • any
  • echo-reply
  • destination-unreachable
  • network-unreachable
  • host-unreachable
  • protocol-unreachable
  • port-unreachable
  • fragmentation-needed
  • source-route-failed
  • network-unknown
  • host-unknown
  • network-prohibited
  • host-prohibited
  • TOS-network-unreachable
  • TOS-host-unreachable
  • communication-prohibited
  • host-precedence-violation
  • precedence-cutoff
  • source-quench
  • redirect
  • network-redirect
  • host-redirect
  • TOS-network-redirect
  • TOS-host-redirect
  • echo-request
  • router-advertisement
  • router-solicitation
  • time-exceeded
  • ttl-zero-during-transit
  • ttl-zero-during-reassembly
  • parameter-problem
  • ip-header-bad
  • required-option-missing
  • timestamp-request
  • timestamp-reply
  • address-mask-request
  • address-mask-reply
  • ping
  • pong
  • ttl-exceeded
ICMP type-name.
inbound_interface
dictionary
Inbound interface.
Only valid in 1.4 and later.
group
string
Interface group.
name
string
Interface name.
Can have wildcards
ipsec
string
    Choices:
  • match-ipsec
  • match-none
  • match-ipsec-in
  • match-ipsec-out
  • match-none-in
  • match-none-out
Inbound ip sec packets.
VyOS 1.4 and older match-ipsec/match-none
VyOS 1.5 and later require -in/-out suffixes
jump_target
string
Jump target if the action is jump.
Only valid in 1.4 and later.
Only valid when action = jump.
limit
dictionary
Rate limit using a token bucket filter.
burst
integer
Maximum number of packets to allow in excess of rate.
rate
dictionary
format for rate (integer/time unit).
any one of second, minute, hour or day may be used to specify time unit.
eg. 1/second implies rule to be matched at an average of once per second.
number
integer
This is the integer value.
unit
string
This is the time unit.
log
string
    Choices:
  • disable
  • enable
Option to log packets matching rule.
number
integer / required
Rule number.
+
+ offload_target + +
+ string +
+
+ +
Match flowtable object.
+
outbound_interface
dictionary
Match outbound interface.
Only valid in 1.4 and later.
group
string
Interface group.
name
string
Interface name.
Can have wildcards
packet_length
list / elements=dictionary
Packet length match.
Only valid in 1.4 and later.
Multiple values from 1 to 65535 and ranges are supported
length
string
Packet length or range.
packet_length_exclude
list / elements=dictionary
Packet length match.
Only valid in 1.4 and later.
Multiple values from 1 to 65535 and ranges are supported
length
string
Packet length or range.
packet_type
string
    Choices:
  • broadcast
  • multicast
  • host
  • other
Packet type match.
protocol
string
Protocol to match (protocol name in /etc/protocols or protocol number or all).
<text> IP protocol name from /etc/protocols (e.g. "tcp" or "udp").
<0-255> IP protocol number.
tcp_udp Both TCP and UDP.
all All IP protocols.
(!)All IP protocols except for the specified name or number.
queue
string
Queue options.
Only valid in 1.4 and later.
Only valid when action = queue.
Can be a queue number or range.
queue_options
string
    Choices:
  • bypass
  • fanout
Queue options.
Only valid in 1.4 and later.
Only valid when action = queue.
recent
dictionary
Parameters for matching recently seen sources.
count
integer
Source addresses seen more than N times.
time
string
Source addresses seen in the last N seconds.
Since 1.4, this is a string of second/minute/hour
source
dictionary
Source parameters.
address
string
Source ip address subnet or range.
IPv4/6 address, subnet or range to match.
Match everything except the specified address, subnet or range.
Source ip address subnet or range.
fqdn
string
Fully qualified domain name.
Available in 1.4 and later.
group
dictionary
Source group.
address_group
string
Group of addresses.
network_group
string
Group of networks.
port_group
string
Group of ports.
mac_address
string
<MAC address> MAC address to match.
<!MAC address> Match everything except the specified MAC address.
port
string
Multiple source ports can be specified as a comma-separated list.
The whole list can also be "negated" using '!'.
For example:'!22,telnet,http,123,1001-1005'.
state
dictionary
Session state.
established
boolean
    Choices:
  • no
  • yes
Established state.
invalid
boolean
    Choices:
  • no
  • yes
Invalid state.
new
boolean
    Choices:
  • no
  • yes
New state.
related
boolean
    Choices:
  • no
  • yes
Related state.
synproxy
dictionary
SYN proxy options.
Only valid in 1.4 and later.
Only valid when action = synproxy.
mss
integer
Adjust MSS (501-65535)
window_scale
integer
Window scale (1-14).
tcp
dictionary
TCP flags to match.
flags
list / elements=dictionary
list of tcp flags to be matched
5.0 breaking change to support 1.4+ and 1.3-
flag
string
    Choices:
  • ack
  • cwr
  • ecn
  • fin
  • psh
  • rst
  • syn
  • urg
  • all
TCP flag to be matched.
syn, ack, fin, rst, urg, psh, all (1.3-)
syn, ack, fin, rst, urg, psh, cwr, ecn (1.4+)
invert
boolean
    Choices:
  • no
  • yes
Invert the match.
time
dictionary
Time to match rule.
monthdays
string
Monthdays to match rule on.
startdate
string
Date to start matching rule.
starttime
string
Time of day to start matching rule.
stopdate
string
Date to stop matching rule.
stoptime
string
Time of day to stop matching rule.
utc
boolean
    Choices:
  • no
  • yes
Interpret times for startdate, stopdate, starttime and stoptime to be UTC.
weekdays
string
Weekdays to match rule on.
running_config
string
This option is used only with state parsed.
The value of this option should be the output received from the VyOS device by executing the command show configuration commands | grep firewall.
The state parsed reads the configuration from running_config option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the parsed key within the result.
state
string
    Choices:
  • merged ←
  • replaced
  • overridden
  • deleted
  • gathered
  • rendered
  • parsed
The state the configuration should be left in

Notes ----- .. note:: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - The provided examples of commands are valid for VyOS 1.4+ - This module works with connection ``ansible.netcommon.network_cli``. See `the VyOS OS Platform Options <../network/user_guide/platform_vyos.html>`_. Examples -------- .. code-block:: yaml # Using deleted to delete firewall rules based on rule-set name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name Downlink" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using deleted to delete firewall rules based on afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # Using deleted to delete all the the firewall rules when provided config is empty # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept rules: - number: 1 action: accept description: Fwipv6-Rule 1 is configured by Ansible ipsec: match-ipsec - number: 2 action: accept description: Fwipv6-Rule 2 is configured by Ansible ipsec: match-ipsec - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv6 name UPLINK rule 1 action 'accept'", # "set firewall ipv6 name UPLINK rule 1", # "set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec'", # "set firewall ipv6 name UPLINK rule 2 action 'accept'", # "set firewall ipv6 name UPLINK rule 2", # "set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: >- Replace device configurations of listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 104 action: reject description: Rule 104 is configured by Ansible ipsec: match-none state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK rule 1", # "delete firewall ipv6 name UPLINK rule 2", # "delete firewall ipv4 name INBOUND rule 102", # "delete firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 104 action 'reject'", # "set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 104", # "set firewall ipv4 name INBOUND rule 104 ipsec 'match-none'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink description: IPv4 INBOUND rule set default_action: accept rules: - number: 501 action: accept description: Rule 501 is configured by Ansible ipsec: match-ipsec - number: 502 action: reject description: Rule 502 is configured by Ansible ipsec: match-ipsec state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK", # "delete firewall ipv4 name INBOUND", # "set firewall ipv4 name Downlink default-action 'accept'", # "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name Downlink rule 501 action 'accept'", # "set firewall ipv4 name Downlink rule 501", # "set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec'", # "set firewall ipv4 name Downlink rule 502 action 'reject'", # "set firewall ipv4 name Downlink rule 502", # "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" # # # "after": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: Gather listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # Using parsed # # - name: Parse the commands for provided configuration vyos.vyos.vyos_firewall_rules: running_config: "set firewall group address-group 'inbound' set firewall ipv4 name Downlink default-action 'accept' set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' set firewall ipv4 name Downlink rule 501 action 'accept' set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' set firewall ipv4 name Downlink rule 502 action 'reject' set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] Return Values ------------- Common return values are documented `here `_, the following are the fields unique to this module: .. raw:: html
Key Returned Description
after
dictionary
when changed
The resulting configuration after module execution.

Sample:
This output will always be in the same format as the module argspec.
before
dictionary
when state is merged, replaced, overridden, deleted or purged
The configuration prior to the module execution.

Sample:
This output will always be in the same format as the module argspec.
commands
list
always
The set of commands pushed to the remote device.

Sample:
["set firewall ipv4 name Downlink default-action 'accept'", "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'", "set firewall ipv4 name Downlink rule 501 action 'accept'", "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'", "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'"]
gathered
list
when state is gathered
Facts about the network resource gathered from the remote device as structured data.

Sample:
This output will always be in the same format as the module argspec.
parsed
list
when state is parsed
The device native config provided in running_config option parsed into structured data as per module argspec.

Sample:
This output will always be in the same format as the module argspec.
rendered
list
when state is rendered
The provided configuration in the task rendered in device-native format (offline).

Sample:
["set firewall ipv4 name Downlink default-action 'accept'", "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'", "set firewall ipv4 name Downlink rule 501 action 'accept'", "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'", "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'"]


Status ------ Authors ~~~~~~~ - Rohit Thakur (@rohitthakur2590) - Gaige B. Paulsen (@gaige) diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py index 6ae17585..d1cf4fe8 100644 --- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py @@ -1,349 +1,351 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The arg spec for the vyos_firewall_rules module """ from __future__ import absolute_import, division, print_function __metaclass__ = type class Firewall_rulesArgs(object): # pylint: disable=R0903 """The arg spec for the vyos_firewall_rules module""" def __init__(self, **kwargs): pass argument_spec = { "config": { "elements": "dict", "options": { "afi": { "choices": ["ipv4", "ipv6"], "required": True, "type": "str", }, "rule_sets": { "elements": "dict", "options": { "default_action": { "choices": [ "drop", "reject", "accept", "jump", ], "type": "str", }, "default_jump_target": {"type": "str"}, "description": {"type": "str"}, "enable_default_log": {"type": "bool"}, "filter": { "choices": [ "input", "output", "forward", ], "type": "str", }, "name": {"type": "str"}, "rules": { "elements": "dict", "options": { "action": { "choices": [ "drop", "reject", "accept", "inspect", "continue", "return", "jump", + "offload", "queue", "synproxy", ], "type": "str", }, "description": {"type": "str"}, "destination": { "options": { "address": {"type": "str"}, "group": { "options": { "address_group": {"type": "str"}, "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "port": {"type": "str"}, }, "type": "dict", }, "disable": { "aliases": ["disabled"], "type": "bool", }, "fragment": { "choices": [ "match-frag", "match-non-frag", ], "type": "str", }, "icmp": { "options": { "code": {"type": "int"}, "type": {"type": "int"}, "type_name": { "choices": [ "any", "echo-reply", "destination-unreachable", "network-unreachable", "host-unreachable", "protocol-unreachable", "port-unreachable", "fragmentation-needed", "source-route-failed", "network-unknown", "host-unknown", "network-prohibited", "host-prohibited", "TOS-network-unreachable", "TOS-host-unreachable", "communication-prohibited", "host-precedence-violation", "precedence-cutoff", "source-quench", "redirect", "network-redirect", "host-redirect", "TOS-network-redirect", "TOS-host-redirect", "echo-request", "router-advertisement", "router-solicitation", "time-exceeded", "ttl-zero-during-transit", "ttl-zero-during-reassembly", "parameter-problem", "ip-header-bad", "required-option-missing", "timestamp-request", "timestamp-reply", "address-mask-request", "address-mask-reply", "ping", "pong", "ttl-exceeded", ], "type": "str", }, }, "type": "dict", }, "inbound_interface": { "options": { "group": {"type": "str"}, "name": {"type": "str"}, }, "type": "dict", }, "ipsec": { "choices": [ "match-ipsec", "match-none", "match-ipsec-in", "match-ipsec-out", "match-none-in", "match-none-out", ], "type": "str", }, "jump_target": {"type": "str"}, "limit": { "options": { "burst": {"type": "int"}, "rate": { "options": { "number": {"type": "int"}, "unit": {"type": "str"}, }, "type": "dict", }, }, "type": "dict", }, "log": { "choices": [ "disable", "enable", ], "type": "str", }, "number": { "required": True, "type": "int", }, + "offload_target": {"type": "str"}, "outbound_interface": { "options": { "group": {"type": "str"}, "name": {"type": "str"}, }, "type": "dict", }, "packet_length": { "elements": "dict", "options": {"length": {"type": "str"}}, "type": "list", }, "packet_length_exclude": { "elements": "dict", "options": {"length": {"type": "str"}}, "type": "list", }, "packet_type": { "choices": [ "broadcast", "multicast", "host", "other", ], "type": "str", }, "protocol": {"type": "str"}, "queue": {"type": "str"}, "queue_options": { "choices": [ "bypass", "fanout", ], "type": "str", }, "recent": { "options": { "count": {"type": "int"}, "time": {"type": "str"}, }, "type": "dict", }, "source": { "options": { "address": {"type": "str"}, "fqdn": {"type": "str"}, "group": { "options": { "address_group": {"type": "str"}, "network_group": {"type": "str"}, "port_group": {"type": "str"}, }, "type": "dict", }, "mac_address": {"type": "str"}, "port": {"type": "str"}, }, "type": "dict", }, "state": { "options": { "established": {"type": "bool"}, "invalid": {"type": "bool"}, "new": {"type": "bool"}, "related": {"type": "bool"}, }, "type": "dict", }, "synproxy": { "options": { "mss": {"type": "int"}, "window_scale": {"type": "int"}, }, "type": "dict", }, "tcp": { "options": { "flags": { "elements": "dict", "options": { "flag": { "choices": [ "ack", "cwr", "ecn", "fin", "psh", "rst", "syn", "urg", "all", ], "type": "str", }, "invert": {"type": "bool"}, }, "type": "list", }, }, "type": "dict", }, "time": { "options": { "monthdays": {"type": "str"}, "startdate": {"type": "str"}, "starttime": {"type": "str"}, "stopdate": {"type": "str"}, "stoptime": {"type": "str"}, "utc": {"type": "bool"}, "weekdays": {"type": "str"}, }, "type": "dict", }, }, "type": "list", }, }, "type": "list", }, }, "type": "list", }, "running_config": {"type": "str"}, "state": { "choices": [ "merged", "replaced", "overridden", "deleted", "gathered", "rendered", "parsed", ], "default": "merged", "type": "str", }, } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py index ddb4af80..d4e173c1 100644 --- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py +++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py @@ -1,794 +1,810 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_firewall_global class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( in_target_not_none, list_diff_want_only, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) -from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( + get_os_version, + load_config, +) class Firewall_global(ConfigBase): """ The vyos_firewall_global class """ gather_subset = ["!all", "!min"] gather_network_resources = ["firewall_global"] def __init__(self, module): super(Firewall_global, self).__init__(module) def get_firewall_global_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) firewall_global_facts = facts["ansible_network_resources"].get("firewall_global") if not firewall_global_facts: return [] return firewall_global_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() + try: + self._module.params["comment"] + except KeyError: + comment = [] + else: + comment = self._module.params["comment"] + if self.state in self.ACTION_STATES: existing_firewall_global_facts = self.get_firewall_global_facts() else: existing_firewall_global_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_firewall_global_facts)) + if commands and self._module._diff: + commit = not self._module.check_mode + diff = load_config(self._module, commands, commit=commit, comment=comment) + if diff: + result["diff"] = {"prepared": str(diff)} + if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_firewall_global_facts = self.get_firewall_global_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) result["parsed"] = self.get_firewall_global_facts(data=running_config) else: changed_firewall_global_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_global_facts if result["changed"]: result["after"] = changed_firewall_global_facts elif self.state == "gathered": result["gathered"] = changed_firewall_global_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_global_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_firewall_global_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "rendered") and not w: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "deleted": commands.extend(self._state_deleted(want=None, have=h)) elif w: if self.state == "merged" or self.state == "rendered": commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, w, h): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if h: commands.extend(self._state_deleted(h, w)) commands.extend(self._state_merged(w, h)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] commands.extend(self._add_global_attr(want, have)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] b_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if want: for key, val in want.items(): if val and key in b_set and not have: commands.append(self._form_attr_cmd(attr=key, opr=False)) elif val and key in b_set and have and key in have and have[key] != val: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) elif not want and have: commands.append(self._compute_command(opr=False)) elif have: for key, val in have.items(): if val and key in b_set: commands.append(self._form_attr_cmd(attr=key, opr=False)) else: commands.extend(self._render_attr_config(want, have, key)) return commands def _render_attr_config(self, w, h, key, opr=False): """ This function invoke the function to extend commands based on the key. :param w: the desired configuration. :param h: the current configuration. :param key: attribute name :param opr: operation :return: list of commands """ commands = [] if key == "ping": commands.extend(self._render_ping(key, w, h, opr=opr)) elif key == "group": commands.extend(self._render_group(key, w, h, opr=opr)) elif key == "state_policy": commands.extend(self._render_state_policy(key, w, h, opr=opr)) elif key == "route_redirects": commands.extend(self._render_route_redirects(key, w, h, opr=opr)) return commands def _add_global_attr(self, w, h, opr=True): """ This function forms the set/delete commands based on the 'opr' type for firewall_global attributes. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated commands list. """ commands = [] w_fg = deepcopy(remove_empties(w)) l_set = ( "config_trap", "validation", "log_martians", "syn_cookies", "twa_hazards_protection", ) if w_fg: for key, val in w_fg.items(): if opr and key in l_set and not (h and self._is_w_same(w_fg, h, key)): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), ) elif not opr: if key and self._is_del(l_set, h): commands.append( self._form_attr_cmd(attr=key, key=self._bool_to_str(val), opr=opr), ) continue if key in l_set and not self._in_target(h, key) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd(attr=key, val=self._bool_to_str(val), opr=opr), ) else: commands.extend(self._render_attr_config(w_fg, h, key, opr)) return commands def _render_ping(self, attr, w, h, opr): """ This function forms the commands for 'ping' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired configuration. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_ping = {} l_set = ("all", "broadcast") if h: h_ping = h.get(attr) or {} if self._is_root_del(w[attr], h_ping, attr): for item, value in h[attr].items(): if not opr and item in l_set: commands.append(self._form_attr_cmd(attr=item, opr=opr)) elif w[attr]: if h and attr in h.keys(): h_ping = h.get(attr) or {} for item, value in w[attr].items(): if ( opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append( self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr), ) elif ( not opr and item in l_set and not (h_ping and self._is_w_same(w[attr], h_ping, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr)) return commands def _render_group(self, attr, w, h, opr): """ This function forms the commands for 'group' attribute based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = {} if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: if h: h_grp = h.get("group") or {} if w: commands.extend(self._render_grp_mem("port_group", w["group"], h_grp, opr)) commands.extend(self._render_grp_mem("address_group", w["group"], h_grp, opr)) commands.extend(self._render_grp_mem("network_group", w["group"], h_grp, opr)) return commands def _render_grp_mem(self, attr, w, h, opr): """ This function forms the commands for group list/members attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_grp = [] w_grp = [] l_set = ("name", "description") if w: w_grp = w.get(attr) or [] if h: h_grp = h.get(attr) or [] if w_grp: for want in w_grp: h = self.search_attrib_in_have(h_grp, want, "name") if "afi" in want and want["afi"] == "ipv6": cmd = self._compute_command(key="group", attr="ipv6-" + attr, opr=opr) else: cmd = self._compute_command(key="group", attr=attr, opr=opr) for key, val in want.items(): if val: if opr and key in l_set and not (h and self._is_w_same(want, h, key)): if key == "name": commands.append(cmd + " " + str(val)) else: commands.append( cmd + " " + want["name"] + " " + key + " '" + str(want[key]) + "'", ) elif not opr and key in l_set: if key == "name" and self._is_grp_del(h, want, key): if len(commands) > 0 and commands[-1] == cmd + " " + want["name"] + " " + self._grp_type( attr, ): commands.pop() commands.append(cmd + " " + want["name"]) continue if not (h and in_target_not_none(h, key)) and not self._is_grp_del( h, want, "name", ): commands.append(cmd + " " + want["name"] + " " + key) elif key == "members": commands.extend( self._render_ports_addrs( key, want, h, opr, cmd, want["name"], attr, ), ) return commands def _render_ports_addrs(self, attr, w, h, opr, cmd, name, type): """ This function forms the commands for port/address/network group members based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param cmd: commands to be prepend. :param name: name of group. :param type: group type. :return: generated list of commands. """ commands = [] have = [] if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: if opr: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)], ) elif not opr and have: members = list_diff_want_only(want, have) for member in members: commands.append( cmd + " " + name + " " + self._grp_type(type) + " " + member[self._get_mem_type(type)], ) elif not opr and not have: commands.append( cmd + " " + name + " " + self._grp_type(type), ) return commands def _get_mem_type(self, group): """ This function returns the member type based on the type of group. """ return "port" if group == "port_group" else "address" def _render_state_policy(self, attr, w, h, opr): """ This function forms the commands for 'state-policy' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): l_set = ("log", "action", "connection_type", "log_level") else: l_set = ("log", "action", "connection_type") if not opr and self._is_root_del(h, w, attr): commands.append(self._form_attr_cmd(attr=attr, opr=opr)) else: w_sp = deepcopy(remove_empties(w)) want = w_sp.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "connection_type") for key, val in w.items(): if val and key != "connection_type": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "log" and LooseVersion( get_os_version(self._module), ) >= LooseVersion("1.4"): commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], attr=key, opr=opr, ), ) else: commands.append( self._form_attr_cmd( key=attr + " " + w["connection_type"], attr=key, val=self._bool_to_str(val), opr=opr, ), ) elif not opr and key in l_set: if not h: commands.append( self._form_attr_cmd( attr=attr + " " + w["connection_type"], opr=opr, ), ) break # delete the whole thing and move on if (not self._in_target(h, key) or h[key] is None) and ( self._in_target(w, key) and w[key] ): # delete if not being replaced and value currently exists commands.append( self._form_attr_cmd( attr=attr + " " + w["connection_type"] + " " + key, val=self._bool_to_str(val), opr=opr, ), ) return commands def _render_route_redirects(self, attr, w, h, opr): """ This function forms the commands for 'route_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] have = [] l_set = ("afi", "ip_src_route") if w: want = w.get(attr) or [] if h: have = h.get(attr) or [] if want: for w in want: h = self.search_attrib_in_have(have, w, "afi") if "afi" in w: afi = w["afi"] else: if h and "afi" in h: afi = h["afi"] else: afi = None afi = None for key, val in w.items(): if val is not None and key != "afi": if opr and key in l_set and not (h and self._is_w_same(w, h, key)): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) elif not opr and key in l_set: if self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) continue if not (h and self._in_target(h, key)) and not self._is_del(l_set, h): commands.append( self._form_attr_cmd( attr=key, val=self._bool_to_str(val), opr=opr, type=afi, ), ) elif key == "icmp_redirects": commands.extend(self._render_icmp_redirects(key, w, h, opr)) return commands def _render_icmp_redirects(self, attr, w, h, opr): """ This function forms the commands for 'icmp_redirects' attributes based on the 'opr'. :param attr: attribute name. :param w: the desired config. :param h: the target config. :param opr: True/False. :return: generated list of commands. """ commands = [] h_red = {} l_set = ("send", "receive") if w and "afi" in w: afi = w["afi"] else: if h and "afi" in h: afi = h["afi"] else: afi = None if w[attr]: if h and attr in h.keys(): h_red = h.get(attr) or {} for item, value in w[attr].items(): if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)): commands.append( self._form_attr_cmd( attr=item, val=self._bool_to_str(value), opr=opr, type=afi, ), ) elif ( not opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)) ): commands.append(self._form_attr_cmd(attr=item, opr=opr, type=afi)) return commands def search_attrib_in_have(self, have, want, attr): """ This function returns the attribute if it is present in target config. :param have: the target config. :param want: the desired config. :param attr: attribute name . :return: attribute/None """ if have: for h in have: if h[attr] == want[attr]: return h return None def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True, type=None): """ This function forms the command for leaf attribute. :param key: parent key. :param attr: attribute name :param value: value :param opr: True/False. :param type: AF type of attribute. :return: generated command. """ command = self._compute_command( key=key, attr=self._map_attrib(attr, type=type), val=val, opr=opr, ) return command def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True): """ This function construct the add/delete command based on passed attributes. :param key: parent key. :param attr: attribute name :param value: value :param remove: True/False. :param opr: True/False. :return: generated command. """ if remove or not opr: cmd = "delete firewall " else: cmd = "set firewall " if ( attr and key != "group" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ): cmd += "global-options " if key: cmd += key.replace("_", "-") + " " if attr: cmd += attr.replace("_", "-") if val and opr: if key == "state_policy" and LooseVersion(get_os_version(self._module)) >= LooseVersion( "1.4", ): cmd += "" else: cmd += " '" + str(val) + "'" return cmd.strip() def _bool_to_str(self, val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ return "enable" if str(val) == "True" else "disable" if str(val) == "False" else val def _grp_type(self, val): """ This function returns the group member type based on value argument. :param val: value. :return: member type. """ return ( "address" if val == "address_group" else "network" if val == "network_group" else "port" ) def _is_w_same(self, w, h, key): """ This function checks whether the key value is same in desired and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(self, h, key): """ This function checks whether the target exist and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False def _is_grp_del(self, w, h, key): """ This function checks whether group needed to be deleted based on desired and target configs. :param w: the desired config. :param h: the target config. :param key: group name. :return: True/False. """ return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_root_del(self, w, h, key): """ This function checks whether a root attribute which can have further child attributes needed to be deleted. :param w: the desired config. :param h: the target config. :param key: attribute name. :return: True/False. """ return True if h and key in h and (not w or key not in w or not w[key]) else False def _is_del(self, b_set, h, key="number"): """ This function checks whether attribute needs to be deleted when operation is false and attribute present in present target config. :param b_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in b_set and not self._in_target(h, key) def _map_attrib(self, attrib, type=None): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "send": if type == "ipv6": regex = "ipv6-send-redirects" else: regex = "send-redirects" elif attrib == "ip_src_route": if type == "ipv6": regex = "ipv6-src-route" elif attrib == "receive": if type == "ipv6": regex = "ipv6-receive-redirects" else: regex = "receive-redirects" elif attrib == "disabled": regex = "disable" elif attrib == "all": regex = "all-ping" elif attrib == "broadcast": regex = "broadcast-ping" elif attrib == "validation": regex = "source-validation" return regex diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py index 436f8b7c..3c768e32 100644 --- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py @@ -1,1185 +1,1186 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_firewall_rules class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( list_diff_want_only, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import ( LooseVersion, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( get_os_version, load_config, ) class Firewall_rules(ConfigBase): """ The vyos_firewall_rules class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "firewall_rules", ] def __init__(self, module): super(Firewall_rules, self).__init__(module) def get_firewall_rules_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) firewall_rules_facts = facts["ansible_network_resources"].get("firewall_rules") if not firewall_rules_facts: return [] return firewall_rules_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() diff = None try: self._module.params["comment"] except KeyError: comment = [] else: comment = self._module.params["comment"] if self.state in self.ACTION_STATES: existing_firewall_rules_facts = self.get_firewall_rules_facts() else: existing_firewall_rules_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(deepcopy(existing_firewall_rules_facts))) if commands and self._module._diff: commit = not self._module.check_mode diff = load_config(self._module, commands, commit=commit, comment=comment) if diff: result["diff"] = {"prepared": str(diff)} if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_firewall_rules_facts = self.get_firewall_rules_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) result["parsed"] = self.get_firewall_rules_facts(data=running_config) else: changed_firewall_rules_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_firewall_rules_facts if result["changed"]: result["after"] = changed_firewall_rules_facts elif self.state == "gathered": result["gathered"] = changed_firewall_rules_facts result["warnings"] = warnings return result def set_config(self, existing_firewall_rules_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] self._prune_stubs(want) have = existing_firewall_rules_facts resp = self.set_state(want, have) return to_list(resp) def set_state(self, w, h): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if self.state in ("merged", "replaced", "overridden", "rendered") and not w: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "overridden": commands.extend(self._state_overridden(w, h)) elif self.state == "deleted": commands.extend(self._state_deleted(w, h)) elif w: if self.state == "merged" or self.state == "rendered": commands.extend(self._state_merged(w, h)) elif self.state == "replaced": commands.extend(self._state_replaced(w, h)) return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: # Iterate over the afi rule sets we already have. for h in have: r_sets = self._get_r_sets(h) # Iterate over each rule set we already have. for rs in r_sets: # In the desired configuration, search for the rule set we # already have (to be replaced by our desired # configuration's rule set). rs_id = self._rs_id(rs, h["afi"]) wanted_rule_set = self.search_r_sets_in_have(want, rs_id, "r_list") if self._is_same_rs(remove_empties(wanted_rule_set), remove_empties(rs)): continue if wanted_rule_set is not None: # Remove the rules that we already have if the wanted # rules exist under the same name. commands.extend( self._add_r_sets( h["afi"], want=rs, have=wanted_rule_set, opr=False, ), ) # Merge the desired configuration into what we already have. commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: for h in have: have_r_sets = self._get_r_sets(h) for rs in have_r_sets: rs_id = self._rs_id(rs, h["afi"]) w = self.search_r_sets_in_have(want, rs_id, "r_list") if self._is_same_rs(remove_empties(w), remove_empties(rs)): continue else: commands.append(self._compute_command(rs_id, remove=True)) # Blank out the only rule set that it is removed. for entry in have: if entry["afi"] == rs_id["afi"] and rs_id["name"]: entry["rule_sets"] = [ rule_set for rule_set in entry["rule_sets"] if rule_set.get("name") != rs_id["name"] ] elif entry["afi"] == rs_id["afi"] and rs_id["filter"]: entry["rule_sets"] = [ rule_set for rule_set in entry["rule_sets"] if rule_set.get("filter") != rs_id["filter"] ] commands.extend(self._state_merged(want, have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] for w in want: r_sets = self._get_r_sets(w) for rs in r_sets: rs_id = self._rs_id(rs, w["afi"]) h = self.search_r_sets_in_have(have, rs_id, "r_list") if self._is_same_rs(remove_empties(h), remove_empties(rs)): continue else: commands.extend(self._add_r_sets(w["afi"], rs, h)) return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] if want: for w in want: r_sets = self._get_r_sets(w) if r_sets: for rs in r_sets: rs_id = self._rs_id(rs, w["afi"]) h = self.search_r_sets_in_have(have, rs_id, "r_list") if h: commands.append(self._compute_command(rs_id, remove=True)) elif have: for h in have: if h["afi"] == w["afi"]: commands.append( self._compute_command(self._rs_id(None, w["afi"]), remove=True), ) elif have: for h in have: r_sets = self._get_r_sets(h) if r_sets: commands.append(self._compute_command(self._rs_id(None, h["afi"]), remove=True)) return commands def _add_r_sets(self, afi, want, have, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rule-sets attributes. :param afi: address type. :param want: desired config. :param have: target config. :param opr: True/False. :return: generated commands list. """ commands = [] l_set = ("description", "default_action", "default_jump_target", "enable_default_log") h_rs = {} h_rules = {} w_rs = deepcopy(remove_empties(want)) w_rules = w_rs.pop("rules", None) rs_id = self._rs_id(want, afi=afi) if have: h_rs = deepcopy(remove_empties(have)) h_rules = h_rs.pop("rules", None) if w_rs: for key, val in w_rs.items(): if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)): if key == "enable_default_log": if val and (not h_rs or key not in h_rs or not h_rs[key]): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs)) else: commands.append(self._add_rs_base_attrib(rs_id, key, w_rs)) elif not opr and key in l_set: if ( key == "enable_default_log" and val and h_rs and (key not in h_rs or not h_rs[key]) ): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr)) elif not (h_rs and self._in_target(h_rs, key)): commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr)) commands.extend(self._add_rules(rs_id, w_rules, h_rules, opr)) if h_rules: have["rules"] = h_rules if w_rules: want["rules"] = w_rules return commands def _add_rules(self, rs_id, w_rules, h_rules, opr=True): """ This function forms the set/delete commands based on the 'opr' type for rules attributes. :param rs_id: rule-set identifier. :param w_rules: desired config. :param h_rules: target config. :param opr: True/False. :return: generated commands list. """ commands = [] l_set = ( "ipsec", "action", "number", "protocol", "fragment", "disable", "description", "jump_target", + "offload_target", ) if w_rules: for w in w_rules: cmd = self._compute_command(rs_id, w["number"], opr=opr) h = self.search_rules_in_have_rs(h_rules, w["number"]) if w != h and self.state == "replaced": h = {} for key, val in w.items(): if val: if opr and key in l_set and not (h and self._is_w_same(w, h, key)): if key == "disable": if not (not val and (not h or key not in h or not h[key])): commands.append(self._add_r_base_attrib(rs_id, key, w)) else: commands.append(self._add_r_base_attrib(rs_id, key, w)) elif not opr: # Note: if you are experiencing sticky configuration on replace # you may need to add an explicit check for the key here. Anything that # doesn't have a custom operation is taken care of by the `l_set` check # below, but I'm not sure how any of the others work. # It's possible that historically the delete was forced (but now it's # checked). if key == "number" and self._is_del(l_set, h): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) continue if ( key == "tcp" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_tcp(key, w, h, cmd, opr)) if ( key == "state" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_state(key, w, h, cmd, opr)) if ( key == "icmp" and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_icmp(key, w, h, cmd, opr)) if ( key in ("packet_length", "packet_length_exclude") and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_packet_length(key, w, h, cmd, opr)) elif key == "disable" and val and h and (key not in h or not h[key]): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) if ( key in ("inbound_interface", "outbound_interface") and val and h and (key not in h or not h[key] or h[key] != w[key]) ): commands.extend(self._add_interface(key, w, h, cmd, opr)) elif ( key in l_set and not (h and self._in_target(h, key)) and not self._is_del(l_set, h) ): commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr)) elif key == "tcp": commands.extend(self._add_tcp(key, w, h, cmd, opr)) elif key == "time": commands.extend(self._add_time(key, w, h, cmd, opr)) elif key == "icmp": commands.extend(self._add_icmp(key, w, h, cmd, opr)) elif key == "state": commands.extend(self._add_state(key, w, h, cmd, opr)) elif key == "log": commands.extend(self._add_log(key, w, h, cmd, opr)) elif key == "limit": commands.extend(self._add_limit(key, w, h, cmd, opr)) elif key == "recent": commands.extend(self._add_recent(key, w, h, cmd, opr)) elif key == "destination" or key == "source": commands.extend(self._add_src_or_dest(key, w, h, cmd, opr)) elif key in ("packet_length", "packet_length_exclude"): commands.extend(self._add_packet_length(key, w, h, cmd, opr)) elif key in ("inbound_interface", "outbound_interface"): commands.extend(self._add_interface(key, w, h, cmd, opr)) return commands def _add_state(self, attr, w, h, cmd, opr): """ This function forms the command for 'state' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_state = {} commands = [] l_set = ("new", "invalid", "related", "established") if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_state and self._is_w_same(w[attr], h_state, item)) ): if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): commands.append(cmd + (" " + attr + " " + item)) else: commands.append( cmd + (" " + attr + " " + item + " " + self._bool_to_str(val)), ) elif not opr and item in l_set and not self._in_target(h_state, item): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_log(self, attr, w, h, cmd, opr): """ This function forms the command for 'log' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_state = {} commands = [] if w[attr]: if h and attr in h.keys(): h_state = h.get(attr) or {} if ( LooseVersion(get_os_version(self._module)) < LooseVersion("1.4") and opr and not (h and self._is_w_same(w, h, attr)) ): commands.append(cmd + " " + attr + " '" + w[attr] + "'") elif ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and opr and not (h and self._is_w_same(w, h, attr)) ): commands.append(cmd + " " + attr) elif not opr and not self._in_target(h_state, w[attr]): commands.append(cmd + (" " + attr + " '" + w[attr] + "'")) return commands def _add_recent(self, attr, w, h, cmd, opr): """ This function forms the command for 'recent' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_recent = {} l_set = ("count", "time") if w[attr]: if h and attr in h.keys(): h_recent = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_recent and self._is_w_same(w[attr], h_recent, item)) ): commands.append(cmd + (" " + attr + " " + item + " " + str(val))) elif ( not opr and item in l_set and not (h_recent and self._in_target(h_recent, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_icmp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'icmp' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_icmp = {} l_set = ("code", "type", "type_name") if w[attr]: if h and attr in h.keys(): h_icmp = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_icmp and self._is_w_same(w[attr], h_icmp, item)) ): if item == "type_name": if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): param_name = "type-name" else: param_name = "type" if "ipv6" in cmd: # ipv6-name or ipv6 commands.append(cmd + (" " + "icmpv6" + " " + param_name + " " + val)) else: commands.append( cmd + (" " + attr + " " + item.replace("_", "-") + " " + val), ) else: if "ipv6" in cmd: # ipv6-name or ipv6 commands.append(cmd + (" " + "icmpv6" + " " + item + " " + str(val))) else: commands.append(cmd + (" " + attr + " " + item + " " + str(val))) elif not opr and item in l_set and not self._in_target(h_icmp, item): commands.append( cmd + (" " + attr + " " + item.replace("_", "-") + " " + str(val)), ) return commands def _add_interface(self, attr, w, h, cmd, opr): """ This function forms the commands for 'interface' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_if = {} l_set = ("name", "group") if w[attr]: if h and attr in h.keys(): h_if = h.get(attr) or {} for item, val in w[attr].items(): if opr and item in l_set and not (h_if and self._is_w_same(w[attr], h_if, item)): commands.append( cmd + (" " + attr.replace("_", "-") + " " + item.replace("_", "-") + " " + val), ) elif not opr and item in l_set and not (h_if and self._in_target(h_if, item)): commands.append( cmd + (" " + attr.replace("_", "-") + " " + item.replace("_", "-")), ) return commands def _add_time(self, attr, w, h, cmd, opr): """ This function forms the commands for 'time' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_time = {} l_set = ( "utc", "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ) if w[attr]: if h and attr in h.keys(): h_time = h.get(attr) or {} for item, val in w[attr].items(): if ( opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): if item == "utc": if not (not val and (not h_time or item not in h_time)): commands.append(cmd + (" " + attr + " " + item)) else: commands.append(cmd + (" " + attr + " " + item + " " + val)) elif ( not opr and item in l_set and not (h_time and self._is_w_same(w[attr], h_time, item)) ): commands.append(cmd + (" " + attr + " " + item)) return commands def _add_tcp_1_4(self, attr, w, h, cmd, opr): """ This function forms the commands for 'tcp' attributes based on the 'opr'. Version 1.4+ :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] have = [] key = "flags" want = [] if w: if w.get(attr): want = w.get(attr).get(key) or [] if h: if h.get(attr): have = h.get(attr).get(key) or [] if want: if opr: flags = list_diff_want_only(want, have) for flag in flags: invert = flag.get("invert", False) commands.append( cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"]), ) elif not opr: flags = list_diff_want_only(want, have) for flag in flags: invert = flag.get("invert", False) commands.append( cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"]), ) return commands def _add_packet_length(self, attr, w, h, cmd, opr): """ This function forms the commands for 'packet_length[_exclude]' attributes based on the 'opr'. If < 1.4, handle tcp attributes. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] have = [] want = [] if w: if w.get(attr): want = w.get(attr) or [] if h: if h.get(attr): have = h.get(attr) or [] attr = attr.replace("_", "-") if want: if opr: lengths = list_diff_want_only(want, have) for l_rec in lengths: commands.append(cmd + " " + attr + " " + str(l_rec["length"])) elif not opr: lengths = list_diff_want_only(want, have) for l_rec in lengths: commands.append(cmd + " " + attr + " " + str(l_rec["length"])) return commands def _tcp_flags_string(self, flags): """ This function forms the tcp flags string. :param flags: flags list. :return: flags string or None. """ if not flags: return "" flag_str = "" for flag in flags: this_flag = flag["flag"].upper() if flag.get("invert", False): this_flag = "!" + this_flag if len(flag_str) > 0: flag_str = ",".join([flag_str, this_flag]) else: flag_str = this_flag return flag_str def _add_tcp(self, attr, w, h, cmd, opr): """ This function forms the commands for 'tcp' attributes based on the 'opr'. If < 1.4, handle tcp attributes. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): return self._add_tcp_1_4(attr, w, h, cmd, opr) h_tcp = {} commands = [] if w[attr]: key = "flags" flags = w[attr].get(key) or {} if flags: if h and key in h[attr].keys(): h_tcp = h[attr].get(key) or {} if flags: flag_str = self._tcp_flags_string(flags) if opr and not (h_tcp and flags == h_tcp): commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str)) if not opr and not (h_tcp and flags == h_tcp): commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str)) return commands def _add_limit(self, attr, w, h, cmd, opr): """ This function forms the commands for 'limit' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ h_limit = {} commands = [] if w[attr]: key = "burst" if ( opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) elif ( not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key + " " + str(w[attr].get(key)))) key = "rate" rate = w[attr].get(key) or {} if rate: if h and key in h[attr].keys(): h_limit = h[attr].get(key) or {} if "unit" in rate and "number" in rate: if opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self._is_w_same(rate, h_limit, "number") ): commands.append( cmd + ( " " + attr + " " + key + " " + str(rate["number"]) + "/" + rate["unit"] ), ) if not opr and not ( h_limit and self._is_w_same(rate, h_limit, "unit") and self._is_w_same(rate, h_limit, "number") ): commands.append(cmd + (" " + attr + " " + key)) return commands def _add_src_or_dest(self, attr, w, h, cmd, opr=True): """ This function forms the commands for 'src/dest' attributes based on the 'opr'. :param attr: attribute name. :param w: base config. :param h: target config. :param cmd: commands to be prepend. :return: generated list of commands. """ commands = [] h_group = {} g_set = ("port_group", "address_group", "network_group") if w[attr]: keys = ("address", "mac_address", "port") for key in keys: if ( opr and key in w[attr].keys() and not (h and attr in h.keys() and self._is_w_same(w[attr], h[attr], key)) ): commands.append( cmd + (" " + attr + " " + key.replace("_", "-") + " " + w[attr].get(key)), ) elif ( not opr and key in w[attr].keys() and not (h and attr in h.keys() and self._in_target(h[attr], key)) ): commands.append(cmd + (" " + attr + " " + key)) key = "group" group = w[attr].get(key) or {} if group: h_group = {} if h and h.get(attr) and key in h[attr].keys(): h_group = h[attr].get(key) for item, val in group.items(): if val: if ( opr and item in g_set and not (h_group and self._is_w_same(group, h_group, item)) ): commands.append( cmd + ( " " + attr + " " + key + " " + item.replace("_", "-") + " " + val ), ) elif ( not opr and item in g_set and not (h_group and self._in_target(h_group, item)) ): commands.append( cmd + (" " + attr + " " + key + " " + item.replace("_", "-")), ) return commands def search_rules_in_have_rs(self, have_rules, r_number): """ This function returns the rule if it is present in target config. :param have: target config. :param rs_id: rule-set identifier. :param r_number: rule-number. :return: rule. """ if have_rules: key = "number" for r in have_rules: if key in r and r[key] == r_number: return r return None def search_r_sets_in_have(self, have, rs_id, type="rule_sets"): """ This function returns the rule-set/rule if it is present in target config. :param have: target config. :param rs_id: rule-identifier. :param type: rule_sets if searching a rule_set and r_list if searching from a rule_list. :return: rule-set/rule. """ if "afi" in rs_id: afi = rs_id["afi"] else: afi = None if rs_id["filter"]: key = "filter" w_value = rs_id["filter"] elif rs_id["name"]: key = "name" w_value = rs_id["name"] else: raise ValueError("id must be specific to name or filter") if type not in ("r_list", "rule_sets"): raise ValueError("type must be rule_sets or r_list") if have: if type == "r_list": for h in have: if h["afi"] == afi: r_sets = self._get_r_sets(h) for rs in r_sets: if key in rs and rs[key] == w_value: return rs else: # searching a ruleset for rs in have: if key in rs and rs[key] == w_value: return rs return None def _get_r_sets(self, item): """ This function returns the list of rule-sets. :param item: config dictionary. :return: list of rule-sets/rules. """ rs_list = [] type = "rule_sets" r_sets = item[type] if r_sets: for rs in r_sets: rs_list.append(rs) return rs_list def _compute_command( self, rs_id, number=None, attrib=None, value=None, remove=False, opr=True, ): """ This function construct the add/delete command based on passed attributes. :param rs_id: rule-set identifier. :param number: rule-number. :param attrib: attribute name. :param value: value. :param remove: True if delete command needed to be construct. :param opr: operation flag. :return: generated command. """ if rs_id["name"] and rs_id["filter"]: raise ValueError("name and filter cannot be used together") if remove or not opr: cmd = "delete firewall " + self._get_fw_type(rs_id["afi"]) else: cmd = "set firewall " + self._get_fw_type(rs_id["afi"]) if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): if rs_id["name"]: cmd += " name " + rs_id["name"] elif rs_id["filter"]: cmd += " " + rs_id["filter"] + " filter" elif rs_id["name"]: cmd += " " + rs_id["name"] if number: cmd += " rule " + str(number) if attrib: if ( LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and attrib == "enable_default_log" ): cmd += " " + "default-log" else: cmd += " " + attrib.replace("_", "-") if value and opr and attrib != "enable_default_log" and attrib != "disable": cmd += " '" + str(value) + "'" return cmd def _add_r_base_attrib(self, rs_id, attr, rule, opr=True): """ This function forms the command for 'rules' attributes which doesn't have further sub attributes. :param rs_id: rule-set identifier. :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ if attr == "number": command = self._compute_command(rs_id, number=rule["number"], opr=opr) else: command = self._compute_command( rs_id=rs_id, number=rule["number"], attrib=attr, value=rule[attr], opr=opr, ) return command def _rs_id(self, have, afi, name=None, filter=None): """ This function returns the rule-set identifier based on the example rule, overriding the components as specified. :param have: example rule. :param afi: address type. :param name: rule-set name. :param filter: filter name. :return: rule-set identifier. """ identifier = {"name": None, "filter": None} if afi: identifier["afi"] = afi else: raise ValueError("afi must be provided") if name: identifier["name"] = name return identifier elif filter: identifier["filter"] = filter return identifier if have: if "name" in have and have["name"]: identifier["name"] = have["name"] return identifier if "filter" in have and have["filter"]: identifier["filter"] = have["filter"] return identifier # raise ValueError("name or filter must be provided or present in have") # unless we want a wildcard return identifier def _add_rs_base_attrib(self, rs_id, attrib, rule, opr=True): """ This function forms the command for 'rule-sets' attributes which don't have further sub attributes. :param rs_id: rule-set identifier. :param attrib: attribute name :param rule: rule config dictionary. :param opr: True/False. :return: generated command. """ command = self._compute_command( rs_id=rs_id, attrib=attrib, value=rule[attrib], opr=opr, ) return command def _bool_to_str(self, val): """ This function converts the bool value into string. :param val: bool value. :return: enable/disable. """ return "enable" if val else "disable" def _get_fw_type(self, afi): """ This function returns the firewall rule-set type based on IP address. :param afi: address type :return: rule-set type. """ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"): return "ipv6" if afi == "ipv6" else "ipv4" return "ipv6-name" if afi == "ipv6" else "name" def _is_del(self, l_set, h, key="number"): """ This function checks whether rule needs to be deleted based on the rule number. :param l_set: attribute set. :param h: target config. :param key: number. :return: True/False. """ return key in l_set and not (h and self._in_target(h, key)) def _is_w_same(self, w, h, key): """ This function checks whether the key value is same in base and target config dictionary. :param w: base config. :param h: target config. :param key:attribute name. :return: True/False. """ return True if h and key in h and h[key] == w[key] else False def _in_target(self, h, key): """ This function checks whether the target exists and key present in target config. :param h: target config. :param key: attribute name. :return: True/False. """ return True if h and key in h else False def _prune_stubs(self, rs): if isinstance(rs, list): for item in rs: self._prune_stubs(item) elif isinstance(rs, dict): keys_to_remove = [ key for key, value in rs.items() if ( (key == "disable" and value is False) or ( key == "log" and value == "disable" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ) or ( key in ["new", "invalid", "related", "established"] and value is False and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") ) ) ] for key in keys_to_remove: del rs[key] for key in rs: self._prune_stubs(rs[key]) def _is_same_rs(self, w, rs): if isinstance(w, dict) and isinstance(rs, dict): if w.keys() != rs.keys(): return False for key in w: if not self._is_same_rs(w[key], rs[key]): return False return True elif isinstance(w, list) and isinstance(rs, list): try: def comparison(x): if "name" in x: return x["name"] if "number" in x: return x["number"] return str(x) sorted_list1 = sorted(w, key=comparison) sorted_list2 = sorted(rs, key=comparison) except TypeError: return False if len(sorted_list1) != len(sorted_list2): return False return all(self._is_same_rs(x, y) for x, y in zip(sorted_list1, sorted_list2)) else: return w == rs diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py index ee709e2a..a23494cd 100644 --- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py @@ -1,319 +1,354 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos_l3_interfaces class It is in this file where the current configuration (as dict) is compared to the provided configuration (as dict) and the command set necessary to bring the current configuration to it's desired end-state is created """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( remove_empties, to_list, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( diff_list_of_dicts, get_interface_type, search_obj_in_list, ) class L3_interfaces(ConfigBase): """ The vyos_l3_interfaces class """ gather_subset = [ "!all", "!min", ] gather_network_resources = [ "l3_interfaces", ] def __init__(self, module): super(L3_interfaces, self).__init__(module) def get_l3_interfaces_facts(self, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) l3_interfaces_facts = facts["ansible_network_resources"].get("l3_interfaces") if not l3_interfaces_facts: return [] return l3_interfaces_facts def execute_module(self): """Execute the module :rtype: A dictionary :returns: The result from module execution """ result = {"changed": False} warnings = list() commands = list() if self.state in self.ACTION_STATES: - existing_l3_interfaces_facts = self.get_l3_interfaces_facts() + existing_l3_interfaces_facts = self.mutate_autoconfig(self.get_l3_interfaces_facts()) else: existing_l3_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_l3_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": - changed_l3_interfaces_facts = self.get_l3_interfaces_facts() + changed_l3_interfaces_facts = self.mutate_autoconfig(self.get_l3_interfaces_facts()) elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] + if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) - result["parsed"] = self.get_l3_interfaces_facts(data=running_config) + result["parsed"] = self.mutate_autoconfig( + self.get_l3_interfaces_facts(data=running_config), + ) else: changed_l3_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_l3_interfaces_facts if result["changed"]: result["after"] = changed_l3_interfaces_facts elif self.state == "gathered": result["gathered"] = changed_l3_interfaces_facts result["warnings"] = warnings return result def set_config(self, existing_l3_interfaces_facts): """Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ want = self._module.params["config"] have = existing_l3_interfaces_facts + resp = self.set_state(want, have) return to_list(resp) def set_state(self, want, have): """Select the appropriate function based on the state provided :param want: the desired configuration as a dictionary :param have: the current configuration as a dictionary :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] state = self._module.params["state"] if state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(state), ) if state == "overridden": commands.extend(self._state_overridden(want=want, have=have)) elif state == "deleted": if not want: for intf in have: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) else: for item in want: obj_in_have = search_obj_in_list(item["name"], have) commands.extend(self._state_deleted(item, obj_in_have)) else: for item in want: name = item["name"] obj_in_have = search_obj_in_list(name, have) if not obj_in_have: obj_in_have = {"name": item["name"]} if state in ("merged", "rendered"): commands.extend(self._state_merged(item, obj_in_have)) elif state == "replaced": commands.extend(self._state_replaced(item, obj_in_have)) + commands = [command.replace("auto-config", "autoconf") for command in commands] return commands def _state_replaced(self, want, have): """The command generator when state is replaced :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] if have: commands.extend(self._state_deleted(want, have)) commands.extend(self._state_merged(want, have)) return commands def _state_overridden(self, want, have): """The command generator when state is overridden :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ commands = [] for intf in have: intf_in_want = search_obj_in_list(intf["name"], want) if not intf_in_want: commands.extend(self._state_deleted({"name": intf["name"]}, intf)) for intf in want: intf_in_have = search_obj_in_list(intf["name"], have) commands.extend(self._state_replaced(intf, intf_in_have)) return commands def _state_merged(self, want, have): """The command generator when state is merged :rtype: A list :returns: the commands necessary to merge the provided into the current configuration """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(remove_empties(have)) want_vifs = want_copy.pop("vifs", []) have_vifs = have_copy.pop("vifs", []) for update in self._get_updates(want_copy, have_copy): for key, value in update.items(): commands.append( self._compute_commands(key=key, value=value, interface=want_copy["name"]), ) if want_vifs: for want_vif in want_vifs: have_vif = search_obj_in_list(want_vif["vlan_id"], have_vifs, key="vlan_id") if not have_vif: have_vif = {} for update in self._get_updates(want_vif, have_vif): for key, value in update.items(): commands.append( self._compute_commands( key=key, value=value, interface=want_copy["name"], vif=want_vif["vlan_id"], ), ) - return commands def _state_deleted(self, want, have): """The command generator when state is deleted :rtype: A list :returns: the commands necessary to remove the current configuration of the provided objects """ commands = [] want_copy = deepcopy(remove_empties(want)) have_copy = deepcopy(have) - want_vifs = want_copy.pop("vifs", []) - have_vifs = have_copy.pop("vifs", []) - - for update in self._get_updates(have_copy, want_copy): - for key, value in update.items(): + if have_copy is not None: + if all(v in (None, {}, []) for k, v in want_copy.items() if k != "name"): commands.append( self._compute_commands( - key=key, - value=value, + key=None, + value=None, interface=want_copy["name"], remove=True, ), ) - - if have_vifs: - for have_vif in have_vifs: - want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") - if not want_vif: - want_vif = {"vlan_id": have_vif["vlan_id"]} - - for update in self._get_updates(have_vif, want_vif): - for key, value in update.items(): - commands.append( - self._compute_commands( - key=key, - interface=want_copy["name"], - value=value, - vif=want_vif["vlan_id"], - remove=True, - ), - ) + return commands + + want_vifs = want_copy.pop("vifs", []) + have_vifs = have_copy.pop("vifs", []) + + if have_vifs: + for have_vif in have_vifs: + want_vif = search_obj_in_list(have_vif["vlan_id"], want_vifs, key="vlan_id") + if not want_vif: + want_vif = {"vlan_id": have_vif["vlan_id"]} + + for update in self._get_updates(have_vif, want_vif): + for key, value in update.items(): + commands.append( + self._compute_commands( + key=key, + interface=want_copy["name"], + value=value, + vif=want_vif["vlan_id"], + remove=True, + ), + ) + + for update in self._get_updates(have_copy, want_copy): + for key, value in update.items(): + commands.append( + self._compute_commands( + key=key, + value=value, + interface=want_copy["name"], + remove=True, + ), + ) return commands def _compute_commands(self, interface, key, vif=None, value=None, remove=False): - intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) + if value == "auto-config" and vif is None: + intf_context = "interfaces {0} {1} ipv6".format( + get_interface_type(interface), + interface, + ) + else: + intf_context = "interfaces {0} {1}".format(get_interface_type(interface), interface) + set_cmd = "set {0}".format(intf_context) del_cmd = "delete {0}".format(intf_context) if vif: - set_cmd = set_cmd + (" vif {0}".format(vif)) - del_cmd = del_cmd + (" vif {0}".format(vif)) + suffix = " ipv6" if value == "auto-config" else "" + set_cmd += f" vif {vif}{suffix}" + del_cmd += f" vif {vif}{suffix}" - if remove: + if remove and key and value: command = "{0} {1} '{2}'".format(del_cmd, key, value) + elif remove and not (key and value): + command = "{0}".format(del_cmd) else: command = "{0} {1} '{2}'".format(set_cmd, key, value) return command def _get_updates(self, want, have): updates = [] updates = diff_list_of_dicts(want.get("ipv4", []), have.get("ipv4", [])) updates.extend(diff_list_of_dicts(want.get("ipv6", []), have.get("ipv6", []))) return updates + + def mutate_autoconfig(self, obj): + if isinstance(obj, dict): + return dict(map(lambda kv: (kv[0], self.mutate_autoconfig(kv[1])), obj.items())) + if isinstance(obj, list): + return list(map(self.mutate_autoconfig, obj)) + if isinstance(obj, str): + return obj.replace("autoconf", "auto-config") + return obj diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py index a6b56345..270b4a62 100644 --- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py +++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py @@ -1,529 +1,544 @@ # # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ The vyos firewall_rules fact class It is in this file the configuration is collected from the device for a given resource, parsed, and the facts tree is populated based on the configuration. """ from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) class Firewall_rulesFacts(object): """The vyos firewall_rules fact class""" def __init__(self, module, subspec="config", options="options"): self._module = module self.argument_spec = Firewall_rulesArgs.argument_spec spec = deepcopy(self.argument_spec) if subspec: if options: facts_argument_spec = spec[subspec][options] else: facts_argument_spec = spec[subspec] else: facts_argument_spec = spec self.generated_spec = utils.generate_dict(facts_argument_spec) def get_device_data(self, connection): return connection.get_config() def populate_facts(self, connection, ansible_facts, data=None): """Populate the facts for firewall_rules :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ if not data: # typically data is populated from the current device configuration # data = connection.get('show running-config | section ^interface') # using mock data instead data = self.get_device_data(connection) # split the config into instances of the resource objs = [] # check 1.4+ first new_rules = True - v6_rules = findall(r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M) + v6_rules = findall( + r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", + data, + M, + ) if not v6_rules: v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M) if v6_rules: new_rules = False - v4_rules = findall(r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M) + v4_rules = findall( + r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", + data, + M, + ) if not v4_rules: v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M) if v4_rules: new_rules = False if v6_rules: if new_rules: config = self.get_rules_post_1_4(data, v6_rules, type="ipv6") else: config = self.get_rules(data, v6_rules, type="ipv6") if config: config = utils.remove_empties(config) objs.append(config) if v4_rules: if new_rules: config = self.get_rules_post_1_4(data, v4_rules, type="ipv4") else: config = self.get_rules(data, v4_rules, type="ipv4") if config: config = utils.remove_empties(config) objs.append(config) ansible_facts["ansible_network_resources"].pop("firewall_rules", None) facts = {} if objs: facts["firewall_rules"] = [] params = utils.validate_config(self.argument_spec, {"config": objs}) for cfg in params["config"]: facts["firewall_rules"].append(utils.remove_empties(cfg)) ansible_facts["ansible_network_resources"].update(facts) return ansible_facts def get_rules(self, data, rules, type): """ This function performs following: - Form regex to fetch 'rule-sets' specific config from data. - Form the rule-set list based on ip address. :param data: configuration. :param rules: list of rule-sets. :param type: ip address type. :return: generated rule-sets configuration. """ r_v4 = [] r_v6 = [] for r in set(rules): name_key = "ipv6-name" if type == "ipv6" else "name" rule_regex = r" %s %s .+$" % (name_key, r.strip("'")) cfg = findall(rule_regex, data, M) fr = self.render_config(cfg, r.strip("'")) fr["name"] = r.strip("'") if type == "ipv6": r_v6.append(fr) else: r_v4.append(fr) if r_v4: config = {"afi": "ipv4", "rule_sets": r_v4} if r_v6: config = {"afi": "ipv6", "rule_sets": r_v6} return config def get_rules_post_1_4(self, data, rules, type): """ This function performs following: - Form regex to fetch 'rule-sets' specific config from data. - Form the rule-set list based on ip address. Specifically for v1.4+ version. :param data: configuration. :param rules: list of rule-sets. :param type: ip address type. :return: generated rule-sets configuration. """ r_v4 = [] r_v6 = [] for kind, name in set(rules): rule_regex = r" %s %s %s .+$" % (type, kind, name.strip("'")) cfg = findall(rule_regex, data, M) fr = self.render_config(cfg, name.strip("'")) if kind == "name": fr["name"] = name.strip("'") elif kind in ("forward", "input", "output"): fr["filter"] = kind else: raise ValueError("Unknown rule kind: %s %s" % kind, name) if type == "ipv6": r_v6.append(fr) else: r_v4.append(fr) if r_v4: config = {"afi": "ipv4", "rule_sets": r_v4} if r_v6: config = {"afi": "ipv6", "rule_sets": r_v6} return config def render_config(self, conf, match): """ Render config as dictionary structure and delete keys from spec for null values :param spec: The facts tree, generated from the argspec :param conf: The configuration :rtype: dictionary :returns: The generated config """ conf = "\n".join(filter(lambda x: x, conf)) - a_lst = ["description", "default_action", "default_jump_target", "enable_default_log", "default_log"] + a_lst = [ + "description", + "default_action", + "default_jump_target", + "enable_default_log", + "default_log", + ] config = self.parse_attr(conf, a_lst, match) if not config: config = {} - if 'default_log' in config: - config['enable_default_log'] = config.pop('default_log') + if "default_log" in config: + config["enable_default_log"] = config.pop("default_log") config["rules"] = self.parse_rules_lst(conf) return config def parse_rules_lst(self, conf): """ This function forms the regex to fetch the 'rules' with in 'rule-sets' :param conf: configuration data. :return: generated rule list configuration. """ r_lst = [] rules = findall(r"rule (?:\'*)(\d+)(?:\'*)", conf, M) if rules: rules_lst = [] for r in set(rules): r_regex = r" %s .+$" % r cfg = "\n".join(findall(r_regex, conf, M)) obj = self.parse_rules(cfg) obj["number"] = int(r) if obj: rules_lst.append(obj) r_lst = sorted(rules_lst, key=lambda i: i["number"]) return r_lst def parse_rules(self, conf): """ This function triggers the parsing of 'rule' attributes. a_lst is a list having rule attributes which doesn't have further sub attributes. :param conf: configuration :return: generated rule configuration dictionary. """ a_lst = [ "ipsec", "log", "action", "protocol", "fragment", "disable", "description", "icmp", "jump_target", + "offload_target", "queue", "queue_options", ] rule = self.parse_attr(conf, a_lst) r_sub = { "tcp": self.parse_tcp(conf), "icmp": self.parse_icmp(conf, "icmp"), "time": self.parse_time(conf, "time"), "limit": self.parse_limit(conf, "limit"), "state": self.parse_state(conf, "state"), "recent": self.parse_recent(conf, "recent"), "source": self.parse_src_or_dest(conf, "source"), "destination": self.parse_src_or_dest(conf, "destination"), "inbound_interface": self.parse_interface(conf, "inbound-interface"), "outbound_interface": self.parse_interface(conf, "outbound-interface"), "packet_length": self.parse_packet_length(conf, "packet-length"), "packet_length_exclude": self.parse_packet_length(conf, "packet-length-exclude"), } rule.update(r_sub) return rule def parse_interface(self, conf, attrib): """ This function triggers the parsing of 'interface' attributes. :param conf: configuration. :param attrib: 'interface'. :return: generated config dictionary. """ a_lst = ["name", "group"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_packet_length(self, conf, attrib=None): """ This function triggers the parsing of 'packet-length' attributes. :param conf: configuration. :param attrib: 'packet-length'. :return: generated config dictionary. """ lengths = [] rule_regex = r"%s (.+)$" % attrib found_lengths = findall(rule_regex, conf, M) if found_lengths: lengths = [] - for l in set(found_lengths): - obj = {"length": l.strip("'")} + for pplen in set(found_lengths): + obj = {"length": pplen.strip("'")} lengths.append(obj) return lengths def parse_src_or_dest(self, conf, attrib=None): """ This function triggers the parsing of 'source or destination' attributes. :param conf: configuration. :param attrib:'source/destination'. :return:generated source/destination configuration dictionary. """ a_lst = ["port", "address", "mac_address"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) cfg_dict["group"] = self.parse_group(conf, attrib + " group") return cfg_dict def parse_recent(self, conf, attrib=None): """ This function triggers the parsing of 'recent' attributes :param conf: configuration. :param attrib: 'recent'. :return: generated config dictionary. """ a_lst = ["time", "count"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_tcp(self, conf): """ This function triggers the parsing of 'tcp' attributes. :param conf: configuration. :param attrib: 'tcp'. :return: generated config dictionary. """ f_lst = [] flags = findall(r"tcp flags (not )?(?:\'*)([\w!,]+)(?:\'*)", conf, M) # for pre 1.4, this is a string including possible commas # and ! as an inverter. For 1.4+ this is a single flag per # command and 'not' as the inverter if flags: flag_lst = [] for n, f in set(flags): f = f.strip("'").lower() if "," in f: # pre 1.4 version with multiple flags fs = f.split(",") for f in fs: if "!" in f: obj = {"flag": f.strip("'!"), "invert": True} else: obj = {"flag": f.strip("'")} flag_lst.append(obj) elif "!" in f: obj = {"flag": f.strip("'!"), "invert": True} flag_lst.append(obj) else: obj = {"flag": f.strip("'")} if n: obj["invert"] = True flag_lst.append(obj) f_lst = sorted(flag_lst, key=lambda i: i["flag"]) return {"flags": f_lst} def parse_time(self, conf, attrib=None): """ This function triggers the parsing of 'time' attributes. :param conf: configuration. :param attrib: 'time'. :return: generated config dictionary. """ a_lst = [ "stopdate", "stoptime", "weekdays", "monthdays", "startdate", "starttime", ] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_state(self, conf, attrib=None): """ This function triggers the parsing of 'state' attributes. :param conf: configuration :param attrib: 'state'. :return: generated config dictionary. """ a_lst = ["new", "invalid", "related", "established"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_group(self, conf, attrib=None): """ This function triggers the parsing of 'group' attributes. :param conf: configuration. :param attrib: 'group'. :return: generated config dictionary. """ a_lst = ["port_group", "address_group", "network_group"] cfg_dict = self.parse_attr(conf, a_lst, match=attrib) return cfg_dict def parse_icmp_attr(self, conf, match): """ This function peforms the following: - parse ICMP arguemnts for firewall rules - consider that older versions may need numbers or letters in type, newer ones are more specific :param conf: configuration. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} if not conf: return config for attrib in ("code", "type", "type-name"): regex = self.map_regex(attrib) if match: regex = match + " " + regex out = search(r"^.*" + regex + " (.+)", conf, M) if out: val = out.group(1).strip("'") - if attrib == 'type-name': - config['type_name'] = val - if attrib == 'code': - config['code'] = int(val) - if attrib == 'type': + if attrib == "type-name": + config["type_name"] = val + if attrib == "code": + config["code"] = int(val) + if attrib == "type": # <1.3 could be # (type), #/# (type/code) or 'type' (type_name) # recent this is only for strings if "/" in val: # type/code (type_no, code) = val.split(".") - config['type'] = type_no - config['code'] = code + config["type"] = type_no + config["code"] = code elif val.isnumeric(): - config['type'] = type_no + config["type"] = type_no else: - config['type_name'] = val + config["type_name"] = val return config def parse_icmp(self, conf, attrib=None): """ This function triggers the parsing of 'icmp' attributes. :param conf: configuration to be parsed. :param attrib: 'icmp'. :return: generated config dictionary. """ cfg_dict = self.parse_icmp_attr(conf, "icmp") - if (len(cfg_dict) == 0): + if len(cfg_dict) == 0: cfg_dict = self.parse_icmp_attr(conf, "icmpv6") return cfg_dict def parse_limit(self, conf, attrib=None): """ This function triggers the parsing of 'limit' attributes. :param conf: configuration to be parsed. :param attrib: 'limit' :return: generated config dictionary. """ cfg_dict = self.parse_attr(conf, ["burst"], match=attrib) cfg_dict["rate"] = self.parse_rate(conf, "rate") return cfg_dict def parse_attr(self, conf, attr_list, match=None): """ This function peforms the following: - Form the regex to fetch the required attribute config. - Type cast the output in desired format. :param conf: configuration. :param attr_list: list of attributes. :param match: parent node/attribute name. :return: generated config dictionary. """ config = {} for attrib in attr_list: regex = self.map_regex(attrib) if match: regex = match + " " + regex if conf: if self.is_bool(attrib): out = conf.find(attrib.replace("_", "-")) dis = conf.find(attrib.replace("_", "-") + " 'disable'") if out >= 1: if dis >= 1: config[attrib] = False else: config[attrib] = True else: out = search(r"^.*" + regex + " (.+)", conf, M) if not out: if attrib == "disable": out = search(r"^.*\d+" + " (disable$)", conf, M) - if attrib == 'log': + if attrib == "log": out = search(r"^.*\d+" + " (log$)", conf, M) if out: val = out.group(1).strip("'") if self.is_num(attrib): val = int(val) if attrib == "disable": val = True if attrib == "log": val = "enable" config[attrib] = val return config def map_regex(self, attrib): """ - This function construct the regex string. - replace the underscore with hyphen. :param attrib: attribute :return: regex string """ regex = attrib.replace("_", "-") if attrib == "disabled": regex = "disable" return regex def is_bool(self, attrib): """ This function looks for the attribute in predefined bool type set. :param attrib: attribute. :return: True/False """ bool_set = ( "new", "invalid", "related", "disabled", "established", "enable_default_log", "default_log", ) return True if attrib in bool_set else False def is_num(self, attrib): """ This function looks for the attribute in predefined integer type set. :param attrib: attribute. :return: True/false. """ num_set = ("time", "code", "type", "count", "burst", "number") return True if attrib in num_set else False def parse_rate(self, conf, match): """ This function triggers the parsing of 'rate' attributes. :param conf: configuration. :param attrib: 'rate' :return: generated config dictionary. """ config = {} out = search(r"^.*" + match + " (.+)", conf, M) if out: val = out.group(1).strip("'") if "/" in val: # number/unit (number, unit) = val.split("/") - config['number'] = number - config['unit'] = unit + config["number"] = number + config["unit"] = unit return config diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py index a7e58e23..7213abd5 100644 --- a/plugins/modules/vyos_firewall_rules.py +++ b/plugins/modules/vyos_firewall_rules.py @@ -1,1674 +1,1679 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2019 Red Hat # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ############################################# # WARNING # ############################################# # # This file is auto generated by the resource # module builder playbook. # # Do not edit this file manually. # # Changes to this file will be over written # by the resource module builder. # # Changes should be made in the model used to # generate this file or in the resource module # builder template. # ############################################# """ The module file for vyos_firewall_rules """ from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = { "metadata_version": "1.1", "status": ["preview"], "supported_by": "network", } DOCUMENTATION = """ --- module: vyos_firewall_rules version_added: '1.0.0' short_description: Firewall rules resource module description: This module manages firewall rule-set attributes on VyOS devices author: - Rohit Thakur (@rohitthakur2590) - Gaige B. Paulsen (@gaige) notes: - Tested against VyOS 1.3.8, 1.4.2, the upcoming 1.5, and the rolling release of spring 2025. - The provided examples of commands are valid for VyOS 1.4+ - This module works with connection C(ansible.netcommon.network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html). options: config: description: A dictionary of Firewall rule-set options. type: list elements: dict suboptions: afi: description: - Specifies the type of rule-set. type: str choices: - ipv4 - ipv6 required: true rule_sets: description: - The Firewall rule-set list. type: list elements: dict suboptions: filter: description: - Filter type (exclusive to "name"). - Supported in 1.4 and later. type: str choices: ['input', 'output', 'forward'] name: description: - Firewall rule set name. - Required for 1.3- and optional for 1.4+. type: str default_action: description: - Default action for rule-set. - drop (Drop if no prior rules are hit (default)) - reject (Drop and notify source if no prior rules are hit) - accept (Accept if no prior rules are hit) - jump (Jump to another rule-set, 1.4+) type: str choices: ['drop', 'reject', 'accept', 'jump'] default_jump_target: description: - Default jump target if the default action is jump. - Only valid in 1.4 and later. - Only valid when default_action = jump. type: str description: description: - Rule set description. type: str enable_default_log: description: - Option to log packets hitting default-action. type: bool rules: description: - A dictionary that specifies the rule-set configurations. type: list elements: dict suboptions: number: description: - Rule number. type: int required: true description: description: - Description of this rule. type: str action: description: - Specifying the action. - inspect is available < 1.4 - continue, return, jump, queue, synproxy are available >= 1.4 type: str choices: - drop - reject - accept - inspect - continue - return - jump + - offload - queue - synproxy destination: description: - Specifying the destination parameters. type: dict suboptions: address: description: - Destination ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Destination ip address subnet or range. type: str group: description: - Destination group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple destination ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str disable: description: - Option to disable firewall rule. - aliased to disabled type: bool aliases: ["disabled"] fragment: description: - IP fragment match. type: str choices: - match-frag - match-non-frag icmp: description: - ICMP type and code information. type: dict suboptions: type_name: description: - ICMP type-name. type: str choices: - any - echo-reply - destination-unreachable - network-unreachable - host-unreachable - protocol-unreachable - port-unreachable - fragmentation-needed - source-route-failed - network-unknown - host-unknown - network-prohibited - host-prohibited - TOS-network-unreachable - TOS-host-unreachable - communication-prohibited - host-precedence-violation - precedence-cutoff - source-quench - redirect - network-redirect - host-redirect - TOS-network-redirect - TOS-host-redirect - echo-request - router-advertisement - router-solicitation - time-exceeded - ttl-zero-during-transit - ttl-zero-during-reassembly - parameter-problem - ip-header-bad - required-option-missing - timestamp-request - timestamp-reply - address-mask-request - address-mask-reply - ping - pong - ttl-exceeded code: description: - ICMP code. type: int type: description: - ICMP type. type: int inbound_interface: description: - Inbound interface. - Only valid in 1.4 and later. type: dict suboptions: name: description: - Interface name. - Can have wildcards type: str group: description: - Interface group. type: str ipsec: description: - Inbound ip sec packets. - VyOS 1.4 and older match-ipsec/match-none - VyOS 1.5 and later require -in/-out suffixes type: str choices: - match-ipsec - match-none - match-ipsec-in - match-ipsec-out - match-none-in - match-none-out jump_target: description: - Jump target if the action is jump. - Only valid in 1.4 and later. - Only valid when action = jump. type: str limit: description: - Rate limit using a token bucket filter. type: dict suboptions: burst: description: - Maximum number of packets to allow in excess of rate. type: int rate: description: - format for rate (integer/time unit). - any one of second, minute, hour or day may be used to specify time unit. - eg. 1/second implies rule to be matched at an average of once per second. type: dict suboptions: number: description: - This is the integer value. type: int unit: description: - This is the time unit. type: str log: description: - Option to log packets matching rule. type: str choices: ['disable', 'enable'] + offload_target: + description: + - Match flowtable object. + type: str outbound_interface: description: - Match outbound interface. - Only valid in 1.4 and later. type: dict suboptions: name: description: - Interface name. - Can have wildcards type: str group: description: - Interface group. type: str packet_length: description: - Packet length match. - Only valid in 1.4 and later. - Multiple values from 1 to 65535 and ranges are supported type: list elements: dict suboptions: length: description: - Packet length or range. type: str packet_length_exclude: description: - Packet length match. - Only valid in 1.4 and later. - Multiple values from 1 to 65535 and ranges are supported type: list elements: dict suboptions: length: description: - Packet length or range. type: str packet_type: description: - Packet type match. type: str choices: ['broadcast', 'multicast', 'host', 'other'] protocol: description: - Protocol to match (protocol name in /etc/protocols or protocol number or all). - IP protocol name from /etc/protocols (e.g. "tcp" or "udp"). - <0-255> IP protocol number. - tcp_udp Both TCP and UDP. - all All IP protocols. - (!)All IP protocols except for the specified name or number. type: str queue: description: - Queue options. - Only valid in 1.4 and later. - Only valid when action = queue. - Can be a queue number or range. type: str queue_options: description: - Queue options. - Only valid in 1.4 and later. - Only valid when action = queue. type: str choices: ['bypass', 'fanout'] recent: description: - Parameters for matching recently seen sources. type: dict suboptions: count: description: - Source addresses seen more than N times. type: int time: description: - Source addresses seen in the last N seconds. - Since 1.4, this is a string of second/minute/hour type: str source: description: - Source parameters. type: dict suboptions: address: description: - Source ip address subnet or range. - IPv4/6 address, subnet or range to match. - Match everything except the specified address, subnet or range. - Source ip address subnet or range. type: str group: description: - Source group. type: dict suboptions: address_group: description: - Group of addresses. type: str network_group: description: - Group of networks. type: str port_group: description: - Group of ports. type: str port: description: - Multiple source ports can be specified as a comma-separated list. - The whole list can also be "negated" using '!'. - For example:'!22,telnet,http,123,1001-1005'. type: str mac_address: description: - MAC address to match. - Match everything except the specified MAC address. type: str fqdn: description: - Fully qualified domain name. - Available in 1.4 and later. type: str state: description: - Session state. type: dict suboptions: established: description: - Established state. type: bool invalid: description: - Invalid state. type: bool new: description: - New state. type: bool related: description: - Related state. type: bool synproxy: description: - SYN proxy options. - Only valid in 1.4 and later. - Only valid when action = synproxy. type: dict suboptions: mss: description: - Adjust MSS (501-65535) type: int window_scale: description: - Window scale (1-14). type: int tcp: description: - TCP flags to match. type: dict suboptions: flags: description: - list of tcp flags to be matched - 5.0 breaking change to support 1.4+ and 1.3- type: list elements: dict suboptions: flag: description: - TCP flag to be matched. - syn, ack, fin, rst, urg, psh, all (1.3-) - syn, ack, fin, rst, urg, psh, cwr, ecn (1.4+) type: str choices: - ack - cwr - ecn - fin - psh - rst - syn - urg - all invert: description: - Invert the match. type: bool time: description: - Time to match rule. type: dict suboptions: utc: description: - Interpret times for startdate, stopdate, starttime and stoptime to be UTC. type: bool monthdays: description: - Monthdays to match rule on. type: str startdate: description: - Date to start matching rule. type: str starttime: description: - Time of day to start matching rule. type: str stopdate: description: - Date to stop matching rule. type: str stoptime: description: - Time of day to stop matching rule. type: str weekdays: description: - Weekdays to match rule on. type: str running_config: description: - This option is used only with state I(parsed). - The value of this option should be the output received from the VyOS device by executing the command B(show configuration commands | grep firewall). - The state I(parsed) reads the configuration from C(running_config) option and transforms it into Ansible structured data as per the resource module's argspec and the value is then returned in the I(parsed) key within the result. type: str state: description: - The state the configuration should be left in type: str choices: - merged - replaced - overridden - deleted - gathered - rendered - parsed default: merged """ EXAMPLES = """ # Using deleted to delete firewall rules based on rule-set name # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name Downlink" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using deleted to delete firewall rules based on afi # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos:~$ show configuration commands| grep firewall # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # Using deleted to delete all the the firewall rules when provided config is empty # # Before state # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # - name: Delete attributes of given firewall rules. vyos.vyos.vyos_firewall_rules: state: deleted # # # ------------------------ # Module Execution Results # ------------------------ # # "before": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # "commands": [ # "delete firewall ipv4 name" # ] # # "after": [] # After state # ------------ # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # Using merged # # Before state: # ------------- # # vyos@vyos# run show configuration commands | grep firewall # set firewall group address-group 'inbound' # - name: Merge the provided configuration with the existing running configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept rules: - number: 1 action: accept description: Fwipv6-Rule 1 is configured by Ansible ipsec: match-ipsec - number: 2 action: accept description: Fwipv6-Rule 2 is configured by Ansible ipsec: match-ipsec - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: merged # # # ------------------------- # Module Execution Result # ------------------------- # # before": [] # # "commands": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv6 name UPLINK rule 1 action 'accept'", # "set firewall ipv6 name UPLINK rule 1", # "set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec'", # "set firewall ipv6 name UPLINK rule 2 action 'accept'", # "set firewall ipv6 name UPLINK rule 2", # "set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible'", # "set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using replaced # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: >- Replace device configurations of listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 104 action: reject description: Rule 104 is configured by Ansible ipsec: match-none state: replaced # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK rule 1", # "delete firewall ipv6 name UPLINK rule 2", # "delete firewall ipv4 name INBOUND rule 102", # "delete firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 104 action 'reject'", # "set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 104", # "set firewall ipv4 name INBOUND rule 104 ipsec 'match-none'" # ] # # "after": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # Using overridden # # Before state # -------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 104 action 'reject' # set firewall ipv4 name INBOUND rule 104 description 'Rule 104 is configured by Ansible' # set firewall ipv4 name INBOUND rule 104 ipsec 'match-none' # - name: Overrides all device configuration with provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv4 rule_sets: - name: Downlink description: IPv4 INBOUND rule set default_action: accept rules: - number: 501 action: accept description: Rule 501 is configured by Ansible ipsec: match-ipsec - number: 502 action: reject description: Rule 502 is configured by Ansible ipsec: match-ipsec state: overridden # # # ------------------------- # Module Execution Result # ------------------------- # # "before": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK" # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 104 is configured by Ansible", # "ipsec": "match-none", # "number": 104 # } # ] # } # ] # } # ] # # "commands": [ # "delete firewall ipv6 name UPLINK", # "delete firewall ipv4 name INBOUND", # "set firewall ipv4 name Downlink default-action 'accept'", # "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name Downlink rule 501 action 'accept'", # "set firewall ipv4 name Downlink rule 501", # "set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec'", # "set firewall ipv4 name Downlink rule 502 action 'reject'", # "set firewall ipv4 name Downlink rule 502", # "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'", # "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" # # # "after": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] # # # After state # ------------ # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv4 name Downlink default-action 'accept' # set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' # set firewall ipv4 name Downlink rule 501 action 'accept' # set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' # set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' # set firewall ipv4 name Downlink rule 502 action 'reject' # set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' # set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec' # Using gathered # # Before state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # - name: Gather listed firewall rules with provided configurations vyos.vyos.vyos_firewall_rules: config: state: gathered # # # ------------------------- # Module Execution Result # ------------------------- # # "gathered": [ # { # "afi": "ipv6", # "rule_sets": [ # { # "default_action": "accept", # "description": "This is ipv6 specific rule-set", # "name": "UPLINK", # "rules": [ # { # "action": "accept", # "description": "Fwipv6-Rule 1 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 1 # }, # { # "action": "accept", # "description": "Fwipv6-Rule 2 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 2 # } # ] # } # ] # }, # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "INBOUND", # "rules": [ # { # "action": "accept", # "description": "Rule 101 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 101 # }, # { # "action": "reject", # "description": "Rule 102 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 102 # }, # { # "action": "accept", # "description": "Rule 103 is configured by Ansible", # "destination": { # "group": { # "address_group": "inbound" # } # }, # "number": 103, # "source": { # "address": "192.0.2.0" # }, # "state": { # "established": true, # "invalid": false, # "new": false, # "related": true # } # } # ] # } # ] # } # ] # # # After state: # ------------- # # vyos@vyos:~$ show configuration commands| grep firewall # set firewall group address-group 'inbound' # set firewall ipv6 name UPLINK default-action 'accept' # set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set' # set firewall ipv6 name UPLINK rule 1 action 'accept' # set firewall ipv6 name UPLINK rule 1 description 'Fwipv6-Rule 1 is configured by Ansible' # set firewall ipv6 name UPLINK rule 1 ipsec 'match-ipsec' # set firewall ipv6 name UPLINK rule 2 action 'accept' # set firewall ipv6 name UPLINK rule 2 description 'Fwipv6-Rule 2 is configured by Ansible' # set firewall ipv6 name UPLINK rule 2 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND default-action 'accept' # set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set' # set firewall ipv4 name INBOUND rule 101 action 'accept' # set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible' # set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 102 action 'reject' # set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible' # set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec' # set firewall ipv4 name INBOUND rule 103 action 'accept' # set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible' # set firewall ipv4 name INBOUND rule 103 destination group address-group 'inbound' # set firewall ipv4 name INBOUND rule 103 source address '192.0.2.0' # set firewall ipv4 name INBOUND rule 103 state established # set firewall ipv4 name INBOUND rule 103 state related # Using rendered # # - name: Render the commands for provided configuration vyos.vyos.vyos_firewall_rules: config: - afi: ipv6 rule_sets: - name: UPLINK description: This is ipv6 specific rule-set default_action: accept - afi: ipv4 rule_sets: - name: INBOUND description: IPv4 INBOUND rule set default_action: accept rules: - number: 101 action: accept description: Rule 101 is configured by Ansible ipsec: match-ipsec - number: 102 action: reject description: Rule 102 is configured by Ansible ipsec: match-ipsec - number: 103 action: accept description: Rule 103 is configured by Ansible destination: group: address_group: inbound source: address: 192.0.2.0 state: established: true new: false invalid: false related: true state: rendered # # # ------------------------- # Module Execution Result # ------------------------- # # # "rendered": [ # "set firewall ipv6 name UPLINK default-action 'accept'", # "set firewall ipv6 name UPLINK description 'This is ipv6 specific rule-set'", # "set firewall ipv4 name INBOUND default-action 'accept'", # "set firewall ipv4 name INBOUND description 'IPv4 INBOUND rule set'", # "set firewall ipv4 name INBOUND rule 101 action 'accept'", # "set firewall ipv4 name INBOUND rule 101", # "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 102 action 'reject'", # "set firewall ipv4 name INBOUND rule 102", # "set firewall ipv4 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 102 ipsec 'match-ipsec'", # "set firewall ipv4 name INBOUND rule 103 description 'Rule 103 is configured by Ansible'", # "set firewall ipv4 name INBOUND rule 103 destination group address-group inbound", # "set firewall ipv4 name INBOUND rule 103", # "set firewall ipv4 name INBOUND rule 103 source address 192.0.2.0", # "set firewall ipv4 name INBOUND rule 103 state established", # "set firewall ipv4 name INBOUND rule 103 state related", # "set firewall ipv4 name INBOUND rule 103 action 'accept'" # ] # Using parsed # # - name: Parse the commands for provided configuration vyos.vyos.vyos_firewall_rules: running_config: "set firewall group address-group 'inbound' set firewall ipv4 name Downlink default-action 'accept' set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set' set firewall ipv4 name Downlink rule 501 action 'accept' set firewall ipv4 name Downlink rule 501 description 'Rule 501 is configured by Ansible' set firewall ipv4 name Downlink rule 501 ipsec 'match-ipsec' set firewall ipv4 name Downlink rule 502 action 'reject' set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible' set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" state: parsed # # # ------------------------- # Module Execution Result # ------------------------- # # # "parsed": [ # { # "afi": "ipv4", # "rule_sets": [ # { # "default_action": "accept", # "description": "IPv4 INBOUND rule set", # "name": "Downlink", # "rules": [ # { # "action": "accept", # "description": "Rule 501 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 501 # }, # { # "action": "reject", # "description": "Rule 502 is configured by Ansible", # "ipsec": "match-ipsec", # "number": 502 # } # ] # } # ] # } # ] """ RETURN = """ before: description: The configuration prior to the module execution. returned: when I(state) is C(merged), C(replaced), C(overridden), C(deleted) or C(purged) type: dict sample: > This output will always be in the same format as the module argspec. after: description: The resulting configuration after module execution. returned: when changed type: dict sample: > This output will always be in the same format as the module argspec. commands: description: The set of commands pushed to the remote device. returned: always type: list sample: - "set firewall ipv4 name Downlink default-action 'accept'" - "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'" - "set firewall ipv4 name Downlink rule 501 action 'accept'" - "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'" - "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" rendered: description: The provided configuration in the task rendered in device-native format (offline). returned: when I(state) is C(rendered) type: list sample: - "set firewall ipv4 name Downlink default-action 'accept'" - "set firewall ipv4 name Downlink description 'IPv4 INBOUND rule set'" - "set firewall ipv4 name Downlink rule 501 action 'accept'" - "set firewall ipv4 name Downlink rule 502 description 'Rule 502 is configured by Ansible'" - "set firewall ipv4 name Downlink rule 502 ipsec 'match-ipsec'" gathered: description: Facts about the network resource gathered from the remote device as structured data. returned: when I(state) is C(gathered) type: list sample: > This output will always be in the same format as the module argspec. parsed: description: The device native config provided in I(running_config) option parsed into structured data as per module argspec. returned: when I(state) is C(parsed) type: list sample: > This output will always be in the same format as the module argspec. """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import ( Firewall_rulesArgs, ) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import ( Firewall_rules, ) def main(): """ Main entry point for module execution :returns: the result form module invocation """ required_if = [ ("state", "merged", ("config",)), ("state", "replaced", ("config",)), ("state", "rendered", ("config",)), ("state", "overridden", ("config",)), ("state", "parsed", ("running_config",)), ] mutually_exclusive = [("config", "running_config")] module = AnsibleModule( argument_spec=Firewall_rulesArgs.argument_spec, required_if=required_if, supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) result = Firewall_rules(module).execute_module() module.exit_json(**result) if __name__ == "__main__": main() diff --git a/tests/integration/targets/vyos_firewall_global/tests/cli/deleted.yaml b/tests/integration/targets/vyos_firewall_global/tests/cli/deleted.yaml index 19fc83de..abcf597c 100644 --- a/tests/integration/targets/vyos_firewall_global/tests/cli/deleted.yaml +++ b/tests/integration/targets/vyos_firewall_global/tests/cli/deleted.yaml @@ -1,44 +1,51 @@ --- - debug: msg: START vyos_firewall_global deleted integration tests ansible_connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Delete attributes of firewall. register: result vyos.vyos.vyos_firewall_global: &id001 config: state: deleted + diff: true - name: Assert that the before dicts were correctly generated assert: that: - "{{ populate == result['before'] }}" + - item in result.diff.prepared + loop: "{{ deleted_diff }}" + loop_control: + loop_var: item - name: Assert that the correct set of commands were generated assert: that: - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that the after dicts were correctly generated assert: that: - "{{ deleted['after'] == result['after'] }}" - name: Delete attributes of given interfaces (IDEMPOTENT) register: result vyos.vyos.vyos_firewall_global: *id001 + diff: true - name: Assert that the previous task was idempotent assert: that: - result.changed == false - result.commands|length == 0 + - result['diff'] is not defined - name: Assert that the before dicts were correctly generated assert: that: - "{{ deleted['after'] == result['before'] }}" always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_firewall_global/tests/cli/merged.yaml b/tests/integration/targets/vyos_firewall_global/tests/cli/merged.yaml index a538476a..d40d37c9 100644 --- a/tests/integration/targets/vyos_firewall_global/tests/cli/merged.yaml +++ b/tests/integration/targets/vyos_firewall_global/tests/cli/merged.yaml @@ -1,42 +1,62 @@ --- - debug: msg: START vyos_firewall_global merged integration tests on connection={{ ansible_connection }} +- include_tasks: _get_version.yaml + - include_tasks: _remove_config.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.vyos_firewall_global: &id001 config: "{{ merged['config'] }}" state: merged - name: Assert that before dicts were correctly generated assert: that: "{{ merged['before'] == result['before'] }}" - name: Assert that correct set of commands were generated assert: that: - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that after dicts was correctly generated assert: that: - "{{ merged['after'] == result['after'] }}" - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) register: result vyos.vyos.vyos_firewall_global: *id001 + diff: true - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false + - result['diff'] is not defined - name: Assert that before dicts were correctly generated assert: that: - "{{ merged['after'] == result['before'] }}" + + - name: Prepare device configurationsfor diff mode test + register: result + vyos.vyos.vyos_firewall_global: + config: "{{ merged['diff_config'] }}" + state: merged + diff: true + + - name: Assert that correct diff of commands were generated + assert: + that: + - item in result.diff.prepared + loop: "{{ merged_diff }}" + loop_control: + loop_var: item + always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_firewall_global/tests/cli/replaced.yaml b/tests/integration/targets/vyos_firewall_global/tests/cli/replaced.yaml index 4c7b4279..26c94b30 100644 --- a/tests/integration/targets/vyos_firewall_global/tests/cli/replaced.yaml +++ b/tests/integration/targets/vyos_firewall_global/tests/cli/replaced.yaml @@ -1,43 +1,61 @@ --- - debug: msg: START vyos_firewall_global replaced integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Replace device configurations of listed firewall with provided configurations register: result vyos.vyos.vyos_firewall_global: &id001 config: "{{ replaced['config'] }}" state: replaced - name: Assert that correct set of commands were generated assert: that: - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that before dicts are correctly generated assert: that: - "{{ populate == result['before'] }}" - name: Assert that after dict is correctly generated assert: that: - "{{ replaced['after'] == result['after'] }}" - name: Replace device configurations of listed firewall with provided configurarions (IDEMPOTENT) register: result vyos.vyos.vyos_firewall_global: *id001 + diff: true - name: Assert that task was idempotent assert: that: - result['changed'] == false + - result['diff'] is not defined - name: Assert that before dict is correctly generated assert: that: - "{{ replaced['after'] == result['before'] }}" + + - name: Replace device configurations and test diff mode + register: result + vyos.vyos.vyos_firewall_global: + config: "{{ replaced['diff_config'] }}" + state: replaced + diff: true + + - name: Assert that correct diff of commands were generated + assert: + that: + - item in result.diff.prepared + loop: "{{ replaced_diff }}" + loop_control: + loop_var: item + always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_firewall_global/vars/main.yaml b/tests/integration/targets/vyos_firewall_global/vars/main.yaml index 363cc9e6..0f041b60 100644 --- a/tests/integration/targets/vyos_firewall_global/vars/main.yaml +++ b/tests/integration/targets/vyos_firewall_global/vars/main.yaml @@ -1,313 +1,384 @@ --- merged: before: [] commands: "{{ merged_commands }}" after: group: address_group: - members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 description: This group has the Management hosts address list name: MGMT-HOSTS afi: ipv4 network_group: - members: - address: 192.0.1.0/24 description: This group has the Management network addresses name: MGMT afi: ipv4 log_martians: true ping: all: true broadcast: true route_redirects: - afi: ipv4 icmp_redirects: receive: false send: true ip_src_route: true syn_cookies: true state_policy: - action: accept connection_type: established log: true - action: reject connection_type: invalid twa_hazards_protection: true validation: strict config: validation: strict log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 + diff_config: + validation: strict + log_martians: true + syn_cookies: true + twa_hazards_protection: true + ping: + all: true + broadcast: true + state_policy: + - connection_type: established + action: accept + log: true + - connection_type: invalid + action: reject + route_redirects: + - afi: ipv4 + ip_src_route: true + icmp_redirects: + send: true + receive: false + group: + address_group: + - name: MGMT-HOSTS + description: This group has the Management hosts address list + members: + - address: 192.0.1.1 + - address: 192.0.1.3 + - address: 192.0.1.5 + network_group: + - name: MGMT + description: This group has the Management network addresses + members: + - address: 1.1.1.1/32 populate: validation: strict log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 afi: ipv4 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 afi: ipv4 replaced: commands: "{{ replaced_commands }}" after: group: address_group: - members: - address: 192.0.3.1 - address: 192.0.3.2 description: Sales office hosts address list name: ENG-HOSTS afi: ipv4 - members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 description: Sales office hosts address list name: SALES-HOSTS afi: ipv4 network_group: - members: - address: 192.0.1.0/24 description: This group has the Management network addresses name: MGMT afi: ipv4 log_martians: true ping: all: true broadcast: true route_redirects: - afi: ipv4 icmp_redirects: receive: false send: true ip_src_route: true state_policy: - action: accept connection_type: established log: true - action: reject connection_type: invalid syn_cookies: true twa_hazards_protection: true validation: strict config: validation: strict log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 + diff_config: + validation: strict + log_martians: true + syn_cookies: true + twa_hazards_protection: true + ping: + all: true + broadcast: true + state_policy: + - connection_type: established + action: accept + log: true + - connection_type: invalid + action: reject + route_redirects: + - afi: ipv4 + ip_src_route: true + icmp_redirects: + send: true + receive: false + group: + address_group: + - name: SALES-HOSTS + description: Sales office hosts address list + members: + - address: 192.0.2.1 + - address: 192.0.2.2 + - address: 192.0.2.3 + - name: ENG-HOSTS + description: Sales office hosts address list + members: + - address: 192.0.3.1 + - address: 192.0.3.2 + network_group: + - name: MGMT + description: This group has the Management network addresses + members: + - address: 1.1.1.1/32 rendered: commands: "{{ rendered_commands }}" config: validation: strict log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: SALES-HOSTS description: Sales office hosts address list members: - address: 192.0.2.1 - address: 192.0.2.2 - address: 192.0.2.3 - name: ENG-HOSTS description: Sales office hosts address list members: - address: 192.0.3.1 - address: 192.0.3.2 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 deleted: commands: "{{ deleted_commands }}" after: [] round_trip: after: validation: strict log_martians: false syn_cookies: false twa_hazards_protection: false ping: all: false broadcast: false state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 afi: ipv4 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 afi: ipv4 forward_config: validation: strict log_martians: true syn_cookies: true twa_hazards_protection: true ping: all: true broadcast: true state_policy: - connection_type: established action: accept log: true - connection_type: invalid action: reject route_redirects: - afi: ipv4 ip_src_route: true icmp_redirects: send: true receive: false group: address_group: - name: MGMT-HOSTS description: This group has the Management hosts address list members: - address: 192.0.1.1 - address: 192.0.1.3 - address: 192.0.1.5 network_group: - name: MGMT description: This group has the Management network addresses members: - address: 192.0.1.0/24 revert_config: validation: strict log_martians: false syn_cookies: false twa_hazards_protection: false ping: all: false broadcast: false diff --git a/tests/integration/targets/vyos_firewall_global/vars/pre-v1_4.yaml b/tests/integration/targets/vyos_firewall_global/vars/pre-v1_4.yaml index db293451..581d4d57 100644 --- a/tests/integration/targets/vyos_firewall_global/vars/pre-v1_4.yaml +++ b/tests/integration/targets/vyos_firewall_global/vars/pre-v1_4.yaml @@ -1,85 +1,95 @@ --- merged_commands: - set firewall group address-group MGMT-HOSTS address 192.0.1.1 - set firewall group address-group MGMT-HOSTS address 192.0.1.3 - set firewall group address-group MGMT-HOSTS address 192.0.1.5 - set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' - set firewall group address-group MGMT-HOSTS - set firewall group network-group MGMT network 192.0.1.0/24 - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT - set firewall ip-src-route 'enable' - set firewall receive-redirects 'disable' - set firewall send-redirects 'enable' - set firewall state-policy established action 'accept' - set firewall state-policy established log 'enable' - set firewall state-policy invalid action 'reject' - set firewall broadcast-ping 'enable' - set firewall all-ping 'enable' - set firewall log-martians 'enable' - set firewall twa-hazards-protection 'enable' - set firewall syn-cookies 'enable' - set firewall source-validation 'strict' populate_commands: - set firewall all-ping 'enable' - set firewall broadcast-ping 'enable' - set firewall group address-group MGMT-HOSTS address '192.0.1.1' - set firewall group address-group MGMT-HOSTS address '192.0.1.3' - set firewall group address-group MGMT-HOSTS address '192.0.1.5' - set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT network '192.0.1.0/24' - set firewall ip-src-route 'enable' - set firewall log-martians 'enable' - set firewall receive-redirects 'disable' - set firewall send-redirects 'enable' - set firewall source-validation 'strict' - set firewall state-policy established action 'accept' - set firewall state-policy established log 'enable' - set firewall state-policy invalid action 'reject' - set firewall syn-cookies 'enable' - set firewall twa-hazards-protection 'enable' replaced_commands: - delete firewall group address-group MGMT-HOSTS - set firewall group address-group SALES-HOSTS address 192.0.2.1 - set firewall group address-group SALES-HOSTS address 192.0.2.2 - set firewall group address-group SALES-HOSTS address 192.0.2.3 - set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' - set firewall group address-group SALES-HOSTS - set firewall group address-group ENG-HOSTS address 192.0.3.1 - set firewall group address-group ENG-HOSTS address 192.0.3.2 - set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' - set firewall group address-group ENG-HOSTS rendered_commands: - set firewall group address-group SALES-HOSTS address 192.0.2.1 - set firewall group address-group SALES-HOSTS address 192.0.2.2 - set firewall group address-group SALES-HOSTS address 192.0.2.3 - set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' - set firewall group address-group SALES-HOSTS - set firewall group address-group ENG-HOSTS address 192.0.3.1 - set firewall group address-group ENG-HOSTS address 192.0.3.2 - set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' - set firewall group address-group ENG-HOSTS - set firewall group network-group MGMT network 192.0.1.0/24 - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT - set firewall ip-src-route 'enable' - set firewall receive-redirects 'disable' - set firewall send-redirects 'enable' - set firewall state-policy established action 'accept' - set firewall state-policy established log 'enable' - set firewall state-policy invalid action 'reject' - set firewall broadcast-ping 'enable' - set firewall all-ping 'enable' - set firewall log-martians 'enable' - set firewall twa-hazards-protection 'enable' - set firewall syn-cookies 'enable' - set firewall source-validation 'strict' deleted_commands: - "delete firewall" parsed_config_file: "_parsed_config_1_3.cfg" + +replaced_diff: + - "+network 1.1.1.1/32" + - "-network 192.0.1.0/24" + +merged_diff: + - "+network 1.1.1.1/32" + +deleted_diff: + - "- network 192.0.1.0/24" diff --git a/tests/integration/targets/vyos_firewall_global/vars/v1_4.yaml b/tests/integration/targets/vyos_firewall_global/vars/v1_4.yaml index d1ee6f2b..68773b2c 100644 --- a/tests/integration/targets/vyos_firewall_global/vars/v1_4.yaml +++ b/tests/integration/targets/vyos_firewall_global/vars/v1_4.yaml @@ -1,85 +1,95 @@ --- merged_commands: - set firewall group address-group MGMT-HOSTS address 192.0.1.1 - set firewall group address-group MGMT-HOSTS address 192.0.1.3 - set firewall group address-group MGMT-HOSTS address 192.0.1.5 - set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' - set firewall group address-group MGMT-HOSTS - set firewall group network-group MGMT network 192.0.1.0/24 - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT - set firewall global-options ip-src-route 'enable' - set firewall global-options receive-redirects 'disable' - set firewall global-options send-redirects 'enable' - set firewall global-options state-policy established action 'accept' - set firewall global-options state-policy established log - set firewall global-options state-policy invalid action 'reject' - set firewall global-options broadcast-ping 'enable' - set firewall global-options all-ping 'enable' - set firewall global-options log-martians 'enable' - set firewall global-options twa-hazards-protection 'enable' - set firewall global-options syn-cookies 'enable' - set firewall global-options source-validation 'strict' populate_commands: - set firewall global-options all-ping 'enable' - set firewall global-options broadcast-ping 'enable' - set firewall group address-group MGMT-HOSTS address '192.0.1.1' - set firewall group address-group MGMT-HOSTS address '192.0.1.3' - set firewall group address-group MGMT-HOSTS address '192.0.1.5' - set firewall group address-group MGMT-HOSTS description 'This group has the Management hosts address list' - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT network '192.0.1.0/24' - set firewall global-options ip-src-route 'enable' - set firewall global-options log-martians 'enable' - set firewall global-options receive-redirects 'disable' - set firewall global-options send-redirects 'enable' - set firewall global-options source-validation 'strict' - set firewall global-options state-policy established action 'accept' - set firewall global-options state-policy established log - set firewall global-options state-policy invalid action 'reject' - set firewall global-options syn-cookies 'enable' - set firewall global-options twa-hazards-protection 'enable' replaced_commands: - delete firewall group address-group MGMT-HOSTS - set firewall group address-group SALES-HOSTS address 192.0.2.1 - set firewall group address-group SALES-HOSTS address 192.0.2.2 - set firewall group address-group SALES-HOSTS address 192.0.2.3 - set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' - set firewall group address-group SALES-HOSTS - set firewall group address-group ENG-HOSTS address 192.0.3.1 - set firewall group address-group ENG-HOSTS address 192.0.3.2 - set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' - set firewall group address-group ENG-HOSTS rendered_commands: - set firewall group address-group SALES-HOSTS address 192.0.2.1 - set firewall group address-group SALES-HOSTS address 192.0.2.2 - set firewall group address-group SALES-HOSTS address 192.0.2.3 - set firewall group address-group SALES-HOSTS description 'Sales office hosts address list' - set firewall group address-group SALES-HOSTS - set firewall group address-group ENG-HOSTS address 192.0.3.1 - set firewall group address-group ENG-HOSTS address 192.0.3.2 - set firewall group address-group ENG-HOSTS description 'Sales office hosts address list' - set firewall group address-group ENG-HOSTS - set firewall group network-group MGMT network 192.0.1.0/24 - set firewall group network-group MGMT description 'This group has the Management network addresses' - set firewall group network-group MGMT - set firewall global-options ip-src-route 'enable' - set firewall global-options receive-redirects 'disable' - set firewall global-options send-redirects 'enable' - set firewall global-options state-policy established action 'accept' - set firewall global-options state-policy established log - set firewall global-options state-policy invalid action 'reject' - set firewall global-options broadcast-ping 'enable' - set firewall global-options all-ping 'enable' - set firewall global-options log-martians 'enable' - set firewall global-options twa-hazards-protection 'enable' - set firewall global-options syn-cookies 'enable' - set firewall global-options source-validation 'strict' deleted_commands: - "delete firewall" parsed_config_file: "_parsed_config_1_4.cfg" + +replaced_diff: + - '+ network "1.1.1.1/32"' + - '- network "192.0.1.0/24"' + +merged_diff: + - '+ network "1.1.1.1/32"' + +deleted_diff: + - '- network "192.0.1.0/24"' diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg index 9232a0a4..a34ac606 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_parsed_config.cfg @@ -1,7 +1,8 @@ set interfaces ethernet eth1 address '192.0.2.10/24' +set interfaces ethernet eth1 vif 102 ipv6 address 'autoconf' set interfaces ethernet eth1 address '2001:db8::10/32' set interfaces ethernet eth1 hw-id '08:00:27:da:67:43' set interfaces ethernet eth2 address '198.51.100.10/24' set interfaces ethernet eth2 hw-id '08:00:27:d8:70:b0' set interfaces ethernet eth2 vif 101 address '198.51.100.130/25' set interfaces ethernet eth2 vif 101 address '2001:db8::20/32' diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml index 6f6c5066..d6ccd116 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml @@ -1,13 +1,13 @@ --- - ansible.builtin.include_tasks: _remove_config.yaml - name: Setup vars: lines: |- set interfaces ethernet eth1 address '192.0.2.14/24' set interfaces ethernet eth2 address '192.0.2.10/24' set interfaces ethernet eth2 address '192.0.2.11/24' - set interfaces ethernet eth2 address '2001:db8::10/32' + set interfaces ethernet eth2 address '2001:db8::10/32' set interfaces ethernet eth2 address '2001:db8::12/32' ansible.netcommon.cli_config: config: "{{ lines }}" diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml index aa5a628f..13774cd5 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml @@ -1,11 +1,14 @@ --- - name: Remove Config vars: - lines: "delete interfaces ethernet \"{{ intf }}\" address\ndelete interfaces ethernet \"{{ intf }}\" vif\n" + lines: | + delete interfaces ethernet "{{ intf }}" address + delete interfaces ethernet "{{ intf }}" vif + delete interfaces ethernet "{{ intf }}" ipv6 loop: - eth1 - eth2 loop_control: loop_var: intf ansible.netcommon.cli_config: config: "{{ lines }}" diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/deleted.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/deleted.yaml index df21b431..000d55e0 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/deleted.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/deleted.yaml @@ -1,46 +1,82 @@ --- - debug: msg: START vyos_interfaces deleted integration tests ansible_connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Delete attributes of given interfaces register: result vyos.vyos.vyos_l3_interfaces: &id001 config: - name: eth1 - - name: eth2 state: deleted - name: Assert that the before dicts were correctly generated assert: that: - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" - name: Assert that the correct set of commands were generated assert: that: - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that the after dicts were correctly generated assert: that: - "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}" - name: Delete attributes of given interfaces (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that the previous task was idempotent assert: that: - result.changed == false - name: Assert that the before dicts were correctly generated assert: that: - "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Prepare delete test for SLAAC auto-config + vyos.vyos.vyos_l3_interfaces: + config: + - name: eth1 + ipv6: + - address: auto-config + - name: eth2 + vifs: + - vlan_id: 101 + ipv6: + - address: auto-config + state: replaced + + - name: Delete SLACC auto-config of given interfaces + register: result + vyos.vyos.vyos_l3_interfaces: + config: + - name: eth1 + - name: eth2 + state: deleted + + - name: Assert that the before dicts were correctly generated (SLAAC) + assert: + that: + - "{{ deleted['before_slaac'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated (SLAAC) + assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated (SLAAC) + assert: + that: + - "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}" + always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml index 41a53d87..0a3db05b 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/gathered.yaml @@ -1,28 +1,60 @@ --- - debug: msg: START vyos_l3_interfaces gathered integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.vyos_l3_interfaces: &id001 config: state: gathered - name: Assert that gathered dicts was correctly generated assert: that: - "{{ populate | symmetric_difference(result['gathered']) |length == 0 }}" - name: Gather the existing running configuration (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false + + - name: Merge the provided configuration for SLAAC tesitng + register: result + vyos.vyos.vyos_l3_interfaces: + config: + - name: eth1 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config + state: merged + + - name: Gather the provided configuration with the existing running configuration + register: result + vyos.vyos.vyos_l3_interfaces: &id002 + config: + state: gathered + + - name: Assert that gathered dicts was correctly generated for SLAAC + assert: + that: + - "{{ populate_slaac | symmetric_difference(result['gathered']) |length == 0 }}" + + - name: Gather the existing running configuration for SLAAC (IDEMPOTENT) + register: result + vyos.vyos.vyos_l3_interfaces: *id002 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/merged.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/merged.yaml index 4ec4d0f2..fcb5e896 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/merged.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/merged.yaml @@ -1,57 +1,61 @@ --- - debug: msg: START vyos_l3_interfaces merged integration tests on connection={{ ansible_connection }} - include_tasks: _remove_config.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.vyos_l3_interfaces: &id001 config: - name: eth1 ipv4: - address: 192.0.2.10/24 ipv6: - address: 2001:db8::10/32 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config - name: eth2 ipv4: - address: 198.51.100.10/24 vifs: - vlan_id: 101 ipv4: - address: 198.51.100.130/25 ipv6: - address: 2001:db8::20/32 state: merged - name: Assert that before dicts were correctly generated assert: that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}" - name: Assert that correct set of commands were generated assert: that: - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that after dicts was correctly generated assert: that: - "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}" - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false - name: Assert that before dicts were correctly generated assert: that: - "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}" always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/overridden.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/overridden.yaml index 8334a610..240d01ac 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/overridden.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/overridden.yaml @@ -1,50 +1,58 @@ --- - debug: msg: START vyos_l3_interfaces merged integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Overrides all device configuration with provided configuration register: result vyos.vyos.vyos_l3_interfaces: &id001 config: - name: eth0 ipv4: - address: dhcp - name: eth1 ipv4: - address: 192.0.2.15/24 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config + + - name: eth2 + ipv6: + - address: auto-config state: overridden - name: Assert that before dicts were correctly generated assert: that: - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" - name: Assert that correct commands were generated assert: that: - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that after dicts were correctly generated assert: that: - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" - name: Overrides all device configuration with provided configurations (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false - name: Assert that before dicts were correctly generated assert: that: - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml index 199879f4..9f5de20a 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/rendered.yaml @@ -1,38 +1,48 @@ --- - debug: msg: START vyos_l3_interfaces rendered integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Structure provided configuration into device specific commands register: result vyos.vyos.vyos_l3_interfaces: &id001 config: - name: eth1 ipv4: - address: 192.0.2.14/24 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config - name: eth2 ipv4: - address: 192.0.2.10/24 - address: 192.0.2.11/24 ipv6: - address: 2001:db8::10/32 - address: 2001:db8::12/32 + vifs: + - vlan_id: 101 + ipv4: + - address: 198.51.100.130/25 + ipv6: + - address: 2001:db8::20/32 state: rendered - name: Assert that correct set of commands were generated assert: that: - "{{ rendered['commands'] | symmetric_difference(result['rendered']) |length == 0 }}" - name: Structure provided configuration into device specific commands (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that the previous task was idempotent assert: that: - result['changed'] == false always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/tests/cli/replaced.yaml b/tests/integration/targets/vyos_l3_interfaces/tests/cli/replaced.yaml index 502175fc..0715172d 100644 --- a/tests/integration/targets/vyos_l3_interfaces/tests/cli/replaced.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/tests/cli/replaced.yaml @@ -1,50 +1,94 @@ --- - debug: msg: START vyos_l3_interfaces replaced integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Replace device configurations of listed interfaces with provided configurations register: result vyos.vyos.vyos_l3_interfaces: &id001 config: - name: eth1 ipv4: - address: 192.0.2.19/24 - name: eth2 ipv6: - address: 2001:db8::11/32 state: replaced - name: Assert that correct set of commands were generated assert: that: - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" - name: Assert that before dicts are correctly generated assert: that: - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" - name: Assert that after dict is correctly generated assert: that: - "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}" - name: Replace device configurations of listed interfaces with provided configurarions (IDEMPOTENT) register: result vyos.vyos.vyos_l3_interfaces: *id001 - name: Assert that task was idempotent assert: that: - result['changed'] == false - name: Assert that before dict is correctly generated assert: that: - "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Replace device configurations (autoconf) + register: result + vyos.vyos.vyos_l3_interfaces: &id002 + config: + - name: eth2 + ipv6: + - address: auto-config + - name: eth1 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config + state: replaced + + - name: Assert that correct set of commands were generated (autoconf) + assert: + that: + - "{{ replaced['af_commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that before dict is correctly generated (autoconf) + assert: + that: + - "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that after dict is correctly generated + assert: + that: + - "{{ replaced['af_after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Replace device autoconf configurations (IDEMPOTENT) + register: result + vyos.vyos.vyos_l3_interfaces: *id002 + + - name: Assert that task was idempotent (autoconf) + assert: + that: + - result['changed'] == false + + - name: Assert that before dict is correctly generated (autoconf) + assert: + that: + - "{{ replaced['af_after'] | symmetric_difference(result['before']) |length == 0 }}" + always: - include_tasks: _remove_config.yaml diff --git a/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml b/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml index e9781e6a..84f1c637 100644 --- a/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml +++ b/tests/integration/targets/vyos_l3_interfaces/vars/main.yaml @@ -1,118 +1,184 @@ --- merged: before: - name: eth0 ipv4: - address: dhcp - name: eth1 - name: eth2 commands: - set interfaces ethernet eth1 address '192.0.2.10/24' - set interfaces ethernet eth1 address '2001:db8::10/32' + - set interfaces ethernet eth1 vif 102 ipv6 address 'autoconf' - set interfaces ethernet eth2 address '198.51.100.10/24' - set interfaces ethernet eth2 vif 101 address '198.51.100.130/25' - set interfaces ethernet eth2 vif 101 address '2001:db8::20/32' after: - - name: eth0 - ipv4: - - address: dhcp - - name: eth1 - ipv4: - - address: 192.0.2.10/24 - ipv6: - - address: 2001:db8::10/32 - - name: eth2 - ipv4: + - ipv4: - address: 198.51.100.10/24 + name: eth2 vifs: - - vlan_id: 101 - ipv4: + - ipv4: - address: 198.51.100.130/25 ipv6: - address: 2001:db8::20/32 + vlan_id: 101 + - ipv4: + - address: dhcp + name: eth0 + - ipv4: + - address: 192.0.2.10/24 + ipv6: + - address: 2001:db8::10/32 + name: eth1 + vifs: + - ipv6: + - address: auto-config + vlan_id: 102 populate: - name: eth1 ipv4: - address: 192.0.2.14/24 - name: eth2 ipv4: - address: 192.0.2.10/24 - address: 192.0.2.11/24 ipv6: - address: 2001:db8::10/32 - address: 2001:db8::12/32 - name: eth0 ipv4: - address: dhcp + +populate_slaac: + - name: eth1 + ipv4: + - address: 192.0.2.14/24 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config + - name: eth2 + ipv4: + - address: 192.0.2.10/24 + - address: 192.0.2.11/24 + ipv6: + - address: 2001:db8::10/32 + - address: 2001:db8::12/32 + - name: eth0 + ipv4: + - address: dhcp replaced: commands: - delete interfaces ethernet eth2 address '192.0.2.10/24' - delete interfaces ethernet eth2 address '192.0.2.11/24' - delete interfaces ethernet eth2 address '2001:db8::10/32' - delete interfaces ethernet eth2 address '2001:db8::12/32' - set interfaces ethernet eth2 address '2001:db8::11/32' - delete interfaces ethernet eth1 address '192.0.2.14/24' - set interfaces ethernet eth1 address '192.0.2.19/24' + af_commands: + - delete interfaces ethernet eth2 address '2001:db8::11/32' + - set interfaces ethernet eth2 ipv6 address 'autoconf' + - delete interfaces ethernet eth1 address '192.0.2.19/24' + - set interfaces ethernet eth1 vif 102 ipv6 address 'autoconf' after: - name: eth2 ipv6: - address: 2001:db8::11/32 - name: eth1 ipv4: - address: 192.0.2.19/24 - name: eth0 ipv4: - address: dhcp + af_after: + - name: eth2 + ipv6: + - address: auto-config + - name: eth1 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config + - name: eth0 + ipv4: + - address: dhcp overridden: commands: - delete interfaces ethernet eth1 address '192.0.2.14/24' - set interfaces ethernet eth1 address '192.0.2.15/24' - - delete interfaces ethernet eth2 address '192.0.2.10/24' + - set interfaces ethernet eth1 vif 102 ipv6 address 'autoconf' - delete interfaces ethernet eth2 address '192.0.2.11/24' - - delete interfaces ethernet eth2 address '2001:db8::10/32' + - delete interfaces ethernet eth2 address '192.0.2.10/24' - delete interfaces ethernet eth2 address '2001:db8::12/32' + - delete interfaces ethernet eth2 address '2001:db8::10/32' + - set interfaces ethernet eth2 ipv6 address 'autoconf' after: - name: eth0 ipv4: - address: dhcp - name: eth1 ipv4: - address: 192.0.2.15/24 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config - name: eth2 + ipv6: + - address: auto-config parsed: after: - name: eth1 ipv4: - address: 192.0.2.10/24 ipv6: - address: 2001:db8::10/32 + vifs: + - vlan_id: 102 + ipv6: + - address: auto-config - name: eth2 ipv4: - address: 198.51.100.10/24 vifs: - vlan_id: 101 ipv4: - address: 198.51.100.130/25 ipv6: - address: 2001:db8::20/32 rendered: commands: - set interfaces ethernet eth1 address '192.0.2.14/24' + - set interfaces ethernet eth1 vif 102 ipv6 address 'autoconf' - set interfaces ethernet eth2 address '192.0.2.11/24' - set interfaces ethernet eth2 address '192.0.2.10/24' - - set interfaces ethernet eth2 address '2001:db8::10/32' - set interfaces ethernet eth2 address '2001:db8::12/32' + - set interfaces ethernet eth2 address '2001:db8::10/32' + - set interfaces ethernet eth2 vif 101 address '198.51.100.130/25' + - set interfaces ethernet eth2 vif 101 address '2001:db8::20/32' deleted: commands: - - delete interfaces ethernet eth1 address '192.0.2.14/24' - - delete interfaces ethernet eth2 address '192.0.2.10/24' - - delete interfaces ethernet eth2 address '192.0.2.11/24' - - delete interfaces ethernet eth2 address '2001:db8::10/32' - - delete interfaces ethernet eth2 address '2001:db8::12/32' + - delete interfaces ethernet eth1 + - delete interfaces ethernet eth2 + commands_slaac: + - delete interfaces ethernet eth1 ipv6 address 'autoconf' + - delete interfaces ethernet eth2 vif 101 ipv6 address 'autoconf' after: - name: eth0 ipv4: - address: dhcp + before_slaac: + - name: eth0 + ipv4: + - address: dhcp - name: eth1 + ipv6: + - address: auto-config - name: eth2 + vifs: + - vlan_id: 101 + ipv6: + - address: auto-config diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg index 3ad6ec97..9665f86b 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config.cfg @@ -1,21 +1,25 @@ +set firewall flowtable test interface 'eth1' +set firewall flowtable test offload 'software' set firewall name V4-INGRESS default-action 'accept' set firewall ipv6-name V6-INGRESS default-action 'accept' set firewall name V4-INGRESS description 'This is IPv4 V4-INGRESS rule set' set firewall name V4-INGRESS enable-default-log set firewall name V4-INGRESS rule 101 protocol 'icmp' set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible' set firewall name V4-INGRESS rule 101 fragment 'match-frag' set firewall name V4-INGRESS rule 101 set firewall name V4-INGRESS rule 101 disable set firewall name V4-INGRESS rule 101 action 'accept' set firewall name V4-INGRESS rule 101 ipsec 'match-ipsec' set firewall name V4-INGRESS rule 101 log 'enable' set firewall name EGRESS default-action 'reject' set firewall ipv6-name EGRESS default-action 'reject' set firewall ipv6-name EGRESS rule 20 set firewall ipv6-name EGRESS rule 20 icmpv6 type 'echo-request' set firewall name MULTIPLE-RULE default-action 'drop' set firewall name MULTIPLE-RULE rule 1 action 'accept' set firewall name MULTIPLE-RULE rule 1 protocol 'all' set firewall name MULTIPLE-RULE rule 2 action 'drop' -set firewall name MULTIPLE-RULE rule 2 protocol 'all' \ No newline at end of file +set firewall name MULTIPLE-RULE rule 2 protocol 'all' +set firewall name V4-OFFLOAD rule 1 action 'offload' +set firewall name V4-OFFLOAD rule 1 offload-target 'test' diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg index 7f63dd78..6f69211a 100644 --- a/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg +++ b/tests/unit/modules/network/vyos/fixtures/vyos_firewall_rules_config_v14.cfg @@ -1,39 +1,44 @@ +set firewall flowtable test interface 'eth1' +set firewall flowtable test offload 'software' set firewall ipv4 name V4-INGRESS default-action 'accept' set firewall ipv6 name V6-INGRESS default-action 'accept' set firewall ipv4 name V4-INGRESS description 'This is IPv4 V4-INGRESS rule set' set firewall ipv4 name V4-INGRESS default-log set firewall ipv4 name V4-INGRESS rule 101 protocol 'icmp' set firewall ipv4 name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible' set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 100 set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 300 set firewall ipv4 name V4-INGRESS rule 101 log set firewall ipv4 name V4-INGRESS rule 101 set firewall ipv4 name V4-INGRESS rule 101 disable set firewall ipv4 name V4-INGRESS rule 101 action 'accept' set firewall ipv4 name EGRESS default-action 'reject' set firewall ipv6 name EGRESS default-action 'reject' set firewall ipv6 name EGRESS rule 20 set firewall ipv6 name EGRESS rule 20 icmpv6 type-name 'echo-request' set firewall ipv6 input filter rule 1 set firewall ipv6 input filter rule 1 action 'jump' set firewall ipv6 input filter rule 1 jump-target 'V6-INGRESS' set firewall ipv6 output filter rule 1 set firewall ipv6 output filter rule 1 action 'jump' set firewall ipv6 output filter rule 1 jump-target 'EGRESS' set firewall ipv4 input filter rule 1 set firewall ipv4 input filter rule 1 action 'jump' set firewall ipv4 input filter rule 1 jump-target 'INGRESS' set firewall ipv4 output filter rule 1 set firewall ipv4 output filter rule 1 action 'jump' set firewall ipv4 output filter rule 1 jump-target 'EGRESS' set firewall ipv4 name IF-TEST rule 10 disable set firewall ipv4 name IF-TEST rule 10 action 'accept' set firewall ipv4 name IF-TEST rule 10 inbound-interface name 'eth0' set firewall ipv4 name IF-TEST rule 10 outbound-interface group 'the-ethers' set firewall ipv4 name IF-TEST rule 10 icmp type-name 'echo-request' set firewall ipv4 name IF-TEST rule 10 state 'related' set firewall ipv4 name MULTIPLE-RULE default-action 'drop' set firewall ipv4 name MULTIPLE-RULE rule 1 action 'accept' set firewall ipv4 name MULTIPLE-RULE rule 1 protocol 'all' set firewall ipv4 name MULTIPLE-RULE rule 2 action 'drop' -set firewall ipv4 name MULTIPLE-RULE rule 2 protocol 'all' \ No newline at end of file +set firewall ipv4 name MULTIPLE-RULE rule 2 protocol 'all' +set firewall ipv4 name V4-OFFLOAD rule 105 +set firewall ipv4 name V4-OFFLOAD rule 105 action 'offload' +set firewall ipv4 name V4-OFFLOAD rule 105 offload-target 'test' diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py index 9a25198f..1af61752 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules13.py @@ -1,1472 +1,1578 @@ # (c) 2016 Red Hat Inc. # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # Make coding more python3-ish from __future__ import absolute_import, division, print_function __metaclass__ = type from unittest.mock import patch from ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_rules from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosFirewallRulesModule13(TestVyosModule): module = vyos_firewall_rules def setUp(self): super(TestVyosFirewallRulesModule13, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config", ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config", ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection", ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", ) self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data", ) self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data", ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.get_os_version", ) self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = "1.2" def tearDown(self): super(TestVyosFirewallRulesModule13, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_get_config.stop() self.mock_load_config.stop() self.mock_execute_show_command.stop() self.mock_get_os_version.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): return load_fixture("vyos_firewall_rules_config.cfg") self.execute_show_command.side_effect = load_from_file def test_vyos_firewall_rule_set_01_merged(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="V6-INBOUND", description="This is IPv6 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V6-OUTBOUND", description="This is IPv6 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), dict( afi="ipv4", rule_sets=[ dict( name="V4-INBOUND", description="This is IPv4 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V4-OUTBOUND", description="This is IPv4 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name V6-INBOUND default-action 'reject'", "set firewall ipv6-name V6-INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name V6-INBOUND enable-default-log", "set firewall ipv6-name V6-OUTBOUND default-action 'accept'", "set firewall ipv6-name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'", "set firewall name V4-INBOUND default-action 'reject'", "set firewall name V4-INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall name V4-INBOUND enable-default-log", "set firewall name V4-OUTBOUND default-action 'accept'", "set firewall name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_01(self): """Test if plugin correctly adds new rules set and a rule with variant attributes""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", description="This is IPv4 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", log="disable", protocol="icmp", fragment="match-frag", disable=True, ), + dict( + number="105", + action="offload", + offload_target="test", + description="Rule 105 is configured by Ansible", + protocol="udp", + ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND default-action 'accept'", "set firewall name INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall name INBOUND enable-default-log", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall name INBOUND rule 101 fragment 'match-frag'", "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 disable", "set firewall name INBOUND rule 101 action 'accept'", "set firewall name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall name INBOUND rule 101 log 'disable'", + "set firewall name INBOUND rule 105", + "set firewall name INBOUND rule 105 action 'offload'", + "set firewall name INBOUND rule 105 offload-target 'test'", + "set firewall name INBOUND rule 105 description 'Rule 105 is configured by Ansible'", + "set firewall name INBOUND rule 105 protocol 'udp'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="192.0.2.0", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="192.0.1.0", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND rule 101 protocol 'tcp'", "set firewall name INBOUND rule 101 destination address 192.0.1.0", "set firewall name INBOUND rule 101 destination port 2124", "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 source address 192.0.2.0", "set firewall name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall name INBOUND rule 101 source port 2127", "set firewall name INBOUND rule 101 state new enable", "set firewall name INBOUND rule 101 state invalid enable", "set firewall name INBOUND rule 101 state related enable", "set firewall name INBOUND rule 101 state established enable", "set firewall name INBOUND rule 101 limit burst 10", "set firewall name INBOUND rule 101 limit rate 20/second", "set firewall name INBOUND rule 101 recent count 10", "set firewall name INBOUND rule 101 recent time 20", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ), ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict( flags=[ dict(flag="all"), - ] + ], ), - ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND rule 101", "set firewall name INBOUND rule 101 tcp flags ALL", "set firewall name INBOUND rule 101 time utc", "set firewall name INBOUND rule 101 time monthdays 2", "set firewall name INBOUND rule 101 time startdate 2020-01-24", "set firewall name INBOUND rule 101 time stopdate 2020-01-28", "set firewall name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall name INBOUND rule 101 time stoptime 13:30:00", "set firewall name INBOUND rule 101 time starttime 13:20:00", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_01(self): """Test if plugin correctly adds new ipv6 rules set and a rule with variant attributes""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disable=True, icmp=dict(type_name="echo-request"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 disable", "set firewall ipv6-name INBOUND rule 101 action 'accept'", "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6-name INBOUND rule 101 icmpv6 type echo-request", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="2001:db8::12", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="2001:db8::11", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND rule 101 protocol 'tcp'", "set firewall ipv6-name INBOUND rule 101 destination address 2001:db8::11", "set firewall ipv6-name INBOUND rule 101 destination port 2124", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 source address 2001:db8::12", "set firewall ipv6-name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall ipv6-name INBOUND rule 101 source port 2127", "set firewall ipv6-name INBOUND rule 101 state new enable", "set firewall ipv6-name INBOUND rule 101 state invalid enable", "set firewall ipv6-name INBOUND rule 101 state related enable", "set firewall ipv6-name INBOUND rule 101 state established enable", "set firewall ipv6-name INBOUND rule 101 limit burst 10", "set firewall ipv6-name INBOUND rule 101 recent count 10", "set firewall ipv6-name INBOUND rule 101 recent time 20", "set firewall ipv6-name INBOUND rule 101 limit rate 20/second", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ), ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall ipv6-name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall ipv6-name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall ipv6-name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall ipv6-name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict( flags=[ dict(flag="all"), - ] + ], ), ), dict( number="102", tcp=dict( flags=[ dict(flag="ack"), dict(flag="syn"), dict(flag="fin", invert=True), ], - ) - ) + ), + ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 tcp flags ALL", "set firewall ipv6-name INBOUND rule 101 time utc", "set firewall ipv6-name INBOUND rule 101 time monthdays 2", "set firewall ipv6-name INBOUND rule 101 time startdate 2020-01-24", "set firewall ipv6-name INBOUND rule 101 time stopdate 2020-01-28", "set firewall ipv6-name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall ipv6-name INBOUND rule 101 time stoptime 13:30:00", "set firewall ipv6-name INBOUND rule 101 time starttime 13:20:00", "set firewall ipv6-name INBOUND rule 102", "set firewall ipv6-name INBOUND rule 102 tcp flags ACK,SYN,!FIN", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="port-unreachable"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND rule 101 icmpv6 type port-unreachable", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type=1, code=1), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND rule 101 icmp type 1", "set firewall name INBOUND rule 101 icmp code 1", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="echo-request"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall name INBOUND rule 101 icmp type-name echo-request", "set firewall name INBOUND rule 101 protocol 'icmp'", "set firewall name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_del_01(self): - """Test if plugin correctly removes existing rule set - """ + """Test if plugin correctly removes existing rule set""" set_module_args( dict( config=[dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")])], state="deleted", ), ) commands = ["delete firewall name V4-INGRESS"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_02(self): - """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6""" set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")]), dict(afi="ipv6", rule_sets=[dict(name="V6-INGRESS")]), + dict(afi="ipv4", rule_sets=[dict(name="V4-OFFLOAD")]), ], state="deleted", ), ) commands = [ + "delete firewall name V4-OFFLOAD", "delete firewall name V4-INGRESS", "delete firewall ipv6-name V6-INGRESS", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_03(self): - """Test if plugin correctly removes existing AFIs, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing AFIs, both ipv4 and ipv6""" set_module_args(dict(config=[], state="deleted")) commands = ["delete firewall name", "delete firewall ipv6-name"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_04(self): - """Test if plugin has no effect on non-existent rule sets - """ + """Test if plugin has no effect on non-existent rule sets""" set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-ING")]), dict(afi="ipv6", rule_sets=[dict(name="V6-ING")]), ], state="deleted", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self): """Test if plugin correctly replaces a particular rule set(s) - without affecting the others + without affecting the others """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="reject", description="Rule 101 is configured by Ansible RM", ipsec="match-ipsec", protocol="tcp", fragment="match-frag", disable=False, ), dict( number="102", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disable=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="EGRESS", default_action="reject", description="This rule-set is configured by Ansible RM", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="replaced", ), ) commands = [ "delete firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall name V4-INGRESS rule 101 fragment 'match-frag'", "set firewall name V4-INGRESS rule 101 ipsec 'match-ipsec'", "set firewall name V4-INGRESS rule 101 protocol 'tcp'", "set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible RM'", "set firewall name V4-INGRESS rule 101 action 'reject'", "set firewall name V4-INGRESS rule 102 disable", "set firewall name V4-INGRESS rule 102 action 'accept'", "set firewall name V4-INGRESS rule 102 protocol 'icmp'", "set firewall name V4-INGRESS rule 102 description 'Rule 102 is configured by Ansible RM'", "set firewall name V4-INGRESS rule 102", "set firewall ipv6-name V6-INGRESS description 'This rule-set is configured by Ansible RM'", "set firewall ipv6-name EGRESS description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self): """Test if plugin correctly replaces a particular rule(s) and rule set attribute(s) - without affecting the others + without affecting the others """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=False, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="replaced", ), ) commands = [ "delete firewall name V4-INGRESS rule 101", "delete firewall name V4-INGRESS enable-default-log", "set firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS rule 101 action 'accept'", "set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall name V4-INGRESS rule 101 disable", "set firewall name V4-INGRESS rule 101 fragment 'match-frag'", "set firewall name V4-INGRESS rule 101 ipsec 'match-ipsec'", "set firewall name V4-INGRESS rule 101 protocol 'icmp'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, log="enable", - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="replaced", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_02(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, - log="enable" + log="enable", + ), + ], + ), + ], + ), + ], + state="replaced", + ), + ) + self.execute_module(changed=False, commands=[]) + + def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_03(self): + """Test if plugin correctly has no effect if there is no change in the configuration""" + set_module_args( + dict( + config=[ + dict( + afi="ipv4", + rule_sets=[ + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", ), ], ), ], ), ], state="replaced", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="merged", ), ) self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_02(self): + """Test if plugin correctly has no effect if there is no change in the configuration""" + set_module_args( + dict( + config=[ + dict( + afi="ipv4", + rule_sets=[ + dict( + name="V4-INGRESS", + description="This is IPv4 V4-INGRESS rule set", + default_action="accept", + enable_default_log=True, + rules=[ + dict( + number="101", + action="accept", + description="Rule 101 is configured by Ansible", + ipsec="match-ipsec", + protocol="icmp", + fragment="match-frag", + disable=True, + ), + ], + ), + dict( + name="EGRESS", + default_action="reject", + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", + ), + ], + ), + ], + ), + dict( + afi="ipv6", + rule_sets=[ + dict( + name="V6-INGRESS", + default_action="accept", + ), + dict( + name="EGRESS", + default_action="reject", + rules=[ + dict( + icmp=dict(type_name="echo-request"), + number=20, + ), + ], + ), + ], + ), + ], + state="merged", + ), + ) + self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self): - """Test if plugin correctly resets the entire rule set if there is a change in the configuration - """ + """Test if plugin correctly resets the entire rule set if there is a change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-IN", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="1", action="reject", description="Rule 1 is configured by Ansible RM", ipsec="match-ipsec", log="enable", protocol="tcp", fragment="match-frag", disable=False, source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), dict( number="2", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disable=True, ), ], ), dict( name="MULTIPLE-RULE", default_action="drop", rules=[ dict( number="1", action="accept", protocol="all", ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-IN", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="V6-EG", default_action="reject", description="This rule-set is configured by Ansible RM", ), ], ), ], state="overridden", ), ) commands = [ "delete firewall ipv6-name V6-INGRESS", "delete firewall ipv6-name EGRESS", "delete firewall name V4-INGRESS", "delete firewall name EGRESS", "delete firewall name MULTIPLE-RULE", + "delete firewall name V4-OFFLOAD", "set firewall name MULTIPLE-RULE default-action 'drop'", "set firewall name MULTIPLE-RULE rule 1", "set firewall name MULTIPLE-RULE rule 1 action 'accept'", "set firewall name MULTIPLE-RULE rule 1 protocol 'all'", "set firewall name V4-IN default-action 'accept'", "set firewall name V4-IN description 'This is IPv4 INGRESS rule set'", "set firewall name V4-IN enable-default-log", "set firewall name V4-IN rule 1 protocol 'tcp'", "set firewall name V4-IN rule 1 log 'enable'", "set firewall name V4-IN rule 1 description 'Rule 1 is configured by Ansible RM'", "set firewall name V4-IN rule 1 fragment 'match-frag'", "set firewall name V4-IN rule 1 source group address-group IN-ADDR-GROUP", "set firewall name V4-IN rule 1 source group network-group IN-NET-GROUP", "set firewall name V4-IN rule 1 source group port-group IN-PORT-GROUP", "set firewall name V4-IN rule 1", "set firewall name V4-IN rule 1 action 'reject'", "set firewall name V4-IN rule 1 ipsec 'match-ipsec'", "set firewall name V4-IN rule 2 disable", "set firewall name V4-IN rule 2 action 'accept'", "set firewall name V4-IN rule 2 protocol 'icmp'", "set firewall name V4-IN rule 2 description 'Rule 102 is configured by Ansible RM'", "set firewall name V4-IN rule 2", "set firewall ipv6-name V6-IN default-action 'accept'", "set firewall ipv6-name V6-IN description 'This rule-set is configured by Ansible RM'", "set firewall ipv6-name V6-EG default-action 'reject'", "set firewall ipv6-name V6-EG description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_02(self): """Test if plugin correctly resets the entire rule set - while removing the absent ones if there is a change in the configuration + while removing the absent ones if there is a change in the configuration """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", protocol="udp", ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="EGRESS", default_action="reject", description="This rule-set is configured by Ansible RM", rules=[ dict( number="20", action="accept", protocol="udp", ), ], ), ], ), ], state="overridden", ), ) commands = [ "delete firewall ipv6-name V6-INGRESS", "delete firewall ipv6-name EGRESS", "delete firewall name V4-INGRESS", "delete firewall name EGRESS", "delete firewall name MULTIPLE-RULE", + "delete firewall name V4-OFFLOAD", "set firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall name V4-INGRESS default-action 'accept'", "set firewall name V4-INGRESS enable-default-log", "set firewall name V4-INGRESS rule 101 protocol 'udp'", "set firewall name V4-INGRESS rule 101 action 'accept'", "set firewall ipv6-name EGRESS description 'This rule-set is configured by Ansible RM'", "set firewall ipv6-name EGRESS default-action 'reject'", "set firewall ipv6-name EGRESS rule 20", "set firewall ipv6-name EGRESS rule 20 protocol 'udp'", - "set firewall ipv6-name EGRESS rule 20 action 'accept'" + "set firewall ipv6-name EGRESS rule 20 action 'accept'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, log="enable", - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), dict( name="MULTIPLE-RULE", default_action="drop", rules=[ dict( number="1", action="accept", protocol="all", ), dict( number="2", action="drop", protocol="all", ), ], ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="1", + action="offload", + offload_target="test", + ), + ], + ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="overridden", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v6_rule_sets_rule_merged_01_version(self): - """Test if plugin correctly adds ipv6 rule set with rules - """ + """Test if plugin correctly adds ipv6 rule set with rules""" self.get_os_version.return_value = "1.3" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disable=True, icmp=dict(type_name="echo-request"), log="enable", ), dict( number="102", action="reject", description="Rule 102 is configured by Ansible", protocol="ipv6-icmp", icmp=dict(type=7), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 disable", "set firewall ipv6-name INBOUND rule 101 action 'accept'", "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6-name INBOUND rule 101 icmpv6 type echo-request", "set firewall ipv6-name INBOUND rule 101 log 'enable'", "set firewall ipv6-name INBOUND rule 102", "set firewall ipv6-name INBOUND rule 102 action 'reject'", "set firewall ipv6-name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6-name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6-name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_jump_rules_merged_01(self): - """Test if plugin correctly adds rule set with a jump action - """ + """Test if plugin correctly adds rule set with a jump action""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set with a jump action", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="jump", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", icmp=dict(type_name="echo-request"), jump_target="PROTECT-RE", - packet_length_exclude=[dict(length=100), dict(length=200)] + packet_length_exclude=[dict(length=100), dict(length=200)], ), dict( number="102", action="reject", description="Rule 102 is configured by Ansible", protocol="ipv6-icmp", icmp=dict(type=7), ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set with a jump action'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6-name INBOUND rule 101 packet-length-exclude 100", "set firewall ipv6-name INBOUND rule 101 packet-length-exclude 200", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6-name INBOUND rule 101 icmpv6 type echo-request", "set firewall ipv6-name INBOUND rule 101 action 'jump'", "set firewall ipv6-name INBOUND rule 101 jump-target 'PROTECT-RE'", "set firewall ipv6-name INBOUND rule 102", "set firewall ipv6-name INBOUND rule 102 action 'reject'", "set firewall ipv6-name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6-name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6-name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_log_merged_01(self): """Test if new stanza log is correctly applied""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set with a log", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", log="enable", ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6-name INBOUND default-action 'accept'", "set firewall ipv6-name INBOUND description 'This is IPv6 INBOUND rule set with a log'", "set firewall ipv6-name INBOUND enable-default-log", "set firewall ipv6-name INBOUND rule 101 log 'enable'", "set firewall ipv6-name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6-name INBOUND rule 101", "set firewall ipv6-name INBOUND rule 101 action 'accept'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_log_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=200)], packet_length=[dict(length=22)], log="enable", ), ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS rule 101", "set firewall name V4-INGRESS rule 101 action 'accept'", "set firewall name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall name V4-INGRESS rule 101 packet-length-exclude 100", "set firewall name V4-INGRESS rule 101 packet-length-exclude 200", "set firewall name V4-INGRESS rule 101 packet-length 22", "set firewall name V4-INGRESS rule 101 log 'enable'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py b/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py index 64884282..0c371b92 100644 --- a/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py +++ b/tests/unit/modules/network/vyos/test_vyos_firewall_rules14.py @@ -1,1896 +1,1931 @@ # (c) 2016 Red Hat Inc. # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # Make coding more python3-ish from __future__ import absolute_import, division, print_function __metaclass__ = type from unittest.mock import patch from ansible_collections.vyos.vyos.plugins.modules import vyos_firewall_rules from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture class TestVyosFirewallRulesModule14(TestVyosModule): module = vyos_firewall_rules def setUp(self): super(TestVyosFirewallRulesModule14, self).setUp() self.mock_get_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config", ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config", ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection", ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( - "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection" + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", ) self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() self.mock_execute_show_command = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.static_routes.static_routes.Static_routesFacts.get_device_data", ) self.mock_execute_show_command = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.firewall_rules.firewall_rules.Firewall_rulesFacts.get_device_data", ) self.execute_show_command = self.mock_execute_show_command.start() self.mock_get_os_version = patch( - "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.get_os_version" + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules.get_os_version", ) self.get_os_version = self.mock_get_os_version.start() self.get_os_version.return_value = "1.4" self.maxDiff = None def tearDown(self): super(TestVyosFirewallRulesModule14, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_get_config.stop() self.mock_load_config.stop() self.mock_execute_show_command.stop() self.mock_get_os_version.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): return load_fixture("vyos_firewall_rules_config_v14.cfg") self.execute_show_command.side_effect = load_from_file def test_vyos_firewall_rule_set_01_merged(self): set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="V6-INBOUND", description="This is IPv6 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V6-OUTBOUND", description="This is IPv6 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), dict( afi="ipv4", rule_sets=[ dict( name="V4-INBOUND", description="This is IPv4 INBOUND rule set", default_action="reject", enable_default_log=True, rules=[], ), dict( name="V4-OUTBOUND", description="This is IPv4 OUTBOUND rule set", default_action="accept", enable_default_log=False, rules=[], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name V6-INBOUND default-action 'reject'", "set firewall ipv6 name V6-INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6 name V6-INBOUND default-log", "set firewall ipv6 name V6-OUTBOUND default-action 'accept'", "set firewall ipv6 name V6-OUTBOUND description 'This is IPv6 OUTBOUND rule set'", "set firewall ipv4 name V4-INBOUND default-action 'reject'", "set firewall ipv4 name V4-INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall ipv4 name V4-INBOUND default-log", "set firewall ipv4 name V4-OUTBOUND default-action 'accept'", "set firewall ipv4 name V4-OUTBOUND description 'This is IPv4 OUTBOUND rule set'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_packet_length_merged_01(self): """Test if new stanza packet-lenght is correctly applied""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set with a jump action", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="jump", description="Rule 101 is configured by Ansible", jump_target="PROTECT-RE", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", "set firewall ipv6 name INBOUND description 'This is IPv6 INBOUND rule set with a jump action'", "set firewall ipv6 name INBOUND default-log", "set firewall ipv6 name INBOUND rule 101 packet-length-exclude 100", "set firewall ipv6 name INBOUND rule 101 packet-length-exclude 200", "set firewall ipv6 name INBOUND rule 101 packet-length 22", "set firewall ipv6 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 action 'jump'", "set firewall ipv6 name INBOUND rule 101 jump-target 'PROTECT-RE'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_packet_length_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101 action 'accept'", "set firewall ipv4 name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 100", "set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 200", "set firewall ipv4 name V4-INGRESS rule 101 packet-length 22", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_filter_merged_01(self): """Test if new stanza filter is correctly applied""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( filter="input", description="This is IPv6 INBOUND rule set with a jump action", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="jump", description="Rule 101 is configured by Ansible", jump_target="PROTECT-RE", packet_length_exclude=[dict(length=100), dict(length=200)], - packet_length=[dict(length=22)] + packet_length=[dict(length=22)], ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 input filter default-action 'accept'", "set firewall ipv6 input filter description 'This is IPv6 INBOUND rule set with a jump action'", "set firewall ipv6 input filter default-log", "set firewall ipv6 input filter rule 101 packet-length-exclude 100", "set firewall ipv6 input filter rule 101 packet-length-exclude 200", "set firewall ipv6 input filter rule 101 packet-length 22", "set firewall ipv6 input filter rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 input filter rule 101", "set firewall ipv6 input filter rule 101 action 'jump'", "set firewall ipv6 input filter rule 101 jump-target 'PROTECT-RE'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_interface_merged_01(self): """Test that the rule with a jump action is correctly applied""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", description="This is IPv6 INBOUND rule set with a jump action", default_action="accept", rules=[ dict( number="101", action="jump", description="Rule 101 is configured by Ansible", jump_target="PROTECT-RE", inbound_interface=dict(name="eth0"), outbound_interface=dict(group="eth1"), ), + dict( + number="105", + action="offload", + offload_target="test", + description="Rule 105 is configured by Ansible", + protocol="udp", + ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name V6-INGRESS description 'This is IPv6 INBOUND rule set with a jump action'", "set firewall ipv6 name V6-INGRESS rule 101 inbound-interface name eth0", "set firewall ipv6 name V6-INGRESS rule 101 outbound-interface group eth1", "set firewall ipv6 name V6-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name V6-INGRESS rule 101", "set firewall ipv6 name V6-INGRESS rule 101 action 'jump'", "set firewall ipv6 name V6-INGRESS rule 101 jump-target 'PROTECT-RE'", + "set firewall ipv6 name V6-INGRESS rule 105", + "set firewall ipv6 name V6-INGRESS rule 105 action 'offload'", + "set firewall ipv6 name V6-INGRESS rule 105 offload-target 'test'", + "set firewall ipv6 name V6-INGRESS rule 105 description 'Rule 105 is configured by Ansible'", + "set firewall ipv6 name V6-INGRESS rule 105 protocol 'udp'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_interface_replace_02(self): """Test that new stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="IF-TEST", description="Changed", rules=[ dict( number="10", action="accept", description="Rule 10 is configured by Ansible", inbound_interface=dict(name="eth1"), ), ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST description 'Changed'", "set firewall ipv4 name IF-TEST rule 10 description 'Rule 10 is configured by Ansible'", - 'set firewall ipv4 name IF-TEST rule 10 inbound-interface name eth1', + "set firewall ipv4 name IF-TEST rule 10 inbound-interface name eth1", "set firewall ipv4 name IF-TEST rule 10 action 'accept'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_01(self): """Test if plugin correctly adds new rules set and a rule with variant attributes""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", description="This is IPv4 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", log="disable", protocol="icmp", fragment="match-frag", disable=True, ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND default-action 'accept'", "set firewall ipv4 name INBOUND description 'This is IPv4 INBOUND rule set'", "set firewall ipv4 name INBOUND default-log", "set firewall ipv4 name INBOUND rule 101", "set firewall ipv4 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv4 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv4 name INBOUND rule 101 fragment 'match-frag'", "set firewall ipv4 name INBOUND rule 101 disable", "set firewall ipv4 name INBOUND rule 101 action 'accept'", "set firewall ipv4 name INBOUND rule 101 ipsec 'match-ipsec'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_02(self): """Test that a rule set is correctly applied - including variant attributes such as state + including variant attributes such as state """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="192.0.2.0", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="192.0.1.0", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND rule 101 protocol 'tcp'", "set firewall ipv4 name INBOUND rule 101 destination port 2124", "set firewall ipv4 name INBOUND rule 101", "set firewall ipv4 name INBOUND rule 101 destination address 192.0.1.0", "set firewall ipv4 name INBOUND rule 101 source address 192.0.2.0", "set firewall ipv4 name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall ipv4 name INBOUND rule 101 source port 2127", "set firewall ipv4 name INBOUND rule 101 state new", "set firewall ipv4 name INBOUND rule 101 state invalid", "set firewall ipv4 name INBOUND rule 101 state related", "set firewall ipv4 name INBOUND rule 101 state established", "set firewall ipv4 name INBOUND rule 101 limit burst 10", "set firewall ipv4 name INBOUND rule 101 limit rate 20/second", "set firewall ipv4 name INBOUND rule 101 recent count 10", "set firewall ipv4 name INBOUND rule 101 recent time 20", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ), ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall ipv4 name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall ipv4 name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall ipv4 name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall ipv4 name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall ipv4 name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall ipv4 name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_04(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict( flags=[ dict(flag="all"), - ] + ], ), - ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND rule 101", "set firewall ipv4 name INBOUND rule 101 tcp flags all", "set firewall ipv4 name INBOUND rule 101 time utc", "set firewall ipv4 name INBOUND rule 101 time monthdays 2", "set firewall ipv4 name INBOUND rule 101 time startdate 2020-01-24", "set firewall ipv4 name INBOUND rule 101 time stopdate 2020-01-28", "set firewall ipv4 name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall ipv4 name INBOUND rule 101 time stoptime 13:30:00", "set firewall ipv4 name INBOUND rule 101 time starttime 13:20:00", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_01(self): """Test if plugin correctly adds new ipv6 rules set and a rule with variant attributes""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disable=True, icmp=dict(type_name="echo-request"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", "set firewall ipv6 name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6 name INBOUND default-log", "set firewall ipv6 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 disable", "set firewall ipv6 name INBOUND rule 101 action 'accept'", "set firewall ipv6 name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6 name INBOUND rule 101 icmpv6 type-name echo-request", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="tcp", source=dict( address="2001:db8::12", mac_address="38:00:25:19:76:0c", port=2127, ), destination=dict(address="2001:db8::11", port=2124), limit=dict( burst=10, rate=dict(number=20, unit="second"), ), recent=dict(count=10, time=20), state=dict( established=True, related=True, invalid=True, new=True, ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND rule 101 protocol 'tcp'", "set firewall ipv6 name INBOUND rule 101 destination address 2001:db8::11", "set firewall ipv6 name INBOUND rule 101 destination port 2124", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 source address 2001:db8::12", "set firewall ipv6 name INBOUND rule 101 source mac-address 38:00:25:19:76:0c", "set firewall ipv6 name INBOUND rule 101 source port 2127", "set firewall ipv6 name INBOUND rule 101 state new", "set firewall ipv6 name INBOUND rule 101 state invalid", "set firewall ipv6 name INBOUND rule 101 state related", "set firewall ipv6 name INBOUND rule 101 state established", "set firewall ipv6 name INBOUND rule 101 limit burst 10", "set firewall ipv6 name INBOUND rule 101 recent count 10", "set firewall ipv6 name INBOUND rule 101 recent time 20", "set firewall ipv6 name INBOUND rule 101 limit rate 20/second", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_03(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", destination=dict( group=dict( address_group="OUT-ADDR-GROUP", network_group="OUT-NET-GROUP", port_group="OUT-PORT-GROUP", ), ), source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND rule 101 source group address-group IN-ADDR-GROUP", "set firewall ipv6 name INBOUND rule 101 source group network-group IN-NET-GROUP", "set firewall ipv6 name INBOUND rule 101 source group port-group IN-PORT-GROUP", "set firewall ipv6 name INBOUND rule 101 destination group address-group OUT-ADDR-GROUP", "set firewall ipv6 name INBOUND rule 101 destination group network-group OUT-NET-GROUP", "set firewall ipv6 name INBOUND rule 101 destination group port-group OUT-PORT-GROUP", "set firewall ipv6 name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_04(self): """Test that the plugin correctly applies configuration - within exsiting rule set + within exsiting rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", time=dict( monthdays="2", startdate="2020-01-24", starttime="13:20:00", stopdate="2020-01-28", stoptime="13:30:00", weekdays="!Sat,Sun", utc=True, ), tcp=dict( flags=[ dict(flag="all"), - ] + ], ), ), dict( number="102", tcp=dict( flags=[ dict(flag="ack"), dict(flag="syn"), dict(flag="fin", invert=True), ], - ) - ) + ), + ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 tcp flags all", "set firewall ipv6 name INBOUND rule 101 time utc", "set firewall ipv6 name INBOUND rule 101 time monthdays 2", "set firewall ipv6 name INBOUND rule 101 time startdate 2020-01-24", "set firewall ipv6 name INBOUND rule 101 time stopdate 2020-01-28", "set firewall ipv6 name INBOUND rule 101 time weekdays !Sat,Sun", "set firewall ipv6 name INBOUND rule 101 time stoptime 13:30:00", "set firewall ipv6 name INBOUND rule 101 time starttime 13:20:00", "set firewall ipv6 name INBOUND rule 102", "set firewall ipv6 name INBOUND rule 102 tcp flags ack", "set firewall ipv6 name INBOUND rule 102 tcp flags not fin", "set firewall ipv6 name INBOUND rule 102 tcp flags syn", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_change_state_01(self): """Test that a rule set is replaced applied without touching the other stanzas - in particular variant attributes such as state + in particular variant attributes such as state """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="IF-TEST", rules=[ dict( number="10", disable=False, action="accept", state=dict( established=True, new=True, ), ), ], ), ], ), ], state="replaced", ), ) commands = [ "delete firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST rule 10", "set firewall ipv4 name IF-TEST rule 10 state established", "set firewall ipv4 name IF-TEST rule 10 state new", "set firewall ipv4 name IF-TEST rule 10 action 'accept'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v6_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing ipv6 rule set + within existing ipv6 rule set """ set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="port-unreachable"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND rule 101 icmpv6 type-name port-unreachable", "set firewall ipv6 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6 name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_01(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type=1, code=1), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND rule 101 icmp type 1", "set firewall ipv4 name INBOUND rule 101 icmp code 1", "set firewall ipv4 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv4 name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_rule_merged_icmp_02(self): """Test if plugin correctly adds new rules with variant attributes - within existing rule set + within existing rule set """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="INBOUND", rules=[ dict( number="101", protocol="icmp", icmp=dict(type_name="echo-request"), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv4 name INBOUND rule 101 icmp type-name echo-request", "set firewall ipv4 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv4 name INBOUND rule 101", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4_rule_sets_del_01(self): - """Test if plugin correctly removes existing rule set - """ + """Test if plugin correctly removes existing rule set""" set_module_args( dict( config=[dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")])], state="deleted", ), ) commands = ["delete firewall ipv4 name V4-INGRESS"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_02(self): - """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6 - """ + """Test if plugin correctly removes existing rule sets, both ipv4 and ipv6""" set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-INGRESS")]), dict(afi="ipv6", rule_sets=[dict(name="V6-INGRESS")]), ], state="deleted", ), ) commands = [ "delete firewall ipv4 name V4-INGRESS", "delete firewall ipv6 name V6-INGRESS", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_03(self): """Test that the plugin correctly deprovisions - variant configuration + variant configuration """ set_module_args(dict(config=[], state="deleted")) commands = ["delete firewall ipv4", "delete firewall ipv6"] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_del_04(self): - """Test if plugin has no effect on non-existent rule sets - """ + """Test if plugin has no effect on non-existent rule sets""" set_module_args( dict( config=[ dict(afi="ipv4", rule_sets=[dict(name="V4-ING")]), dict(afi="ipv6", rule_sets=[dict(name="V6-ING")]), ], state="deleted", ), ) self.execute_module(changed=False, commands=[]) + def test_vyos_firewall_v4_rule_sets_del_05(self): + """Test if plugin correctly removes existing rule set""" + set_module_args( + dict( + config=[dict(afi="ipv4", rule_sets=[dict(name="V4-OFFLOAD")])], + state="deleted", + ), + ) + commands = ["delete firewall ipv4 name V4-OFFLOAD"] + self.execute_module(changed=True, commands=commands) + def test_vyos_firewall_v4v6_rule_sets_rule_rep_01(self): """Test if plugin correctly replaces a particular rule set(s) - without affecting the others + without affecting the others """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="reject", description="Rule 101 is configured by Ansible RM", ipsec="match-ipsec", protocol="tcp", fragment="match-frag", disable=False, ), dict( number="102", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disable=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="EGRESS", default_action="reject", description="This rule-set is configured by Ansible RM", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="replaced", ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall ipv4 name V4-INGRESS rule 101 fragment 'match-frag'", "set firewall ipv4 name V4-INGRESS rule 101 ipsec 'match-ipsec'", "set firewall ipv4 name V4-INGRESS rule 101 protocol 'tcp'", "set firewall ipv4 name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible RM'", "set firewall ipv4 name V4-INGRESS rule 101 action 'reject'", "set firewall ipv4 name V4-INGRESS rule 102 disable", "set firewall ipv4 name V4-INGRESS rule 102 action 'accept'", "set firewall ipv4 name V4-INGRESS rule 102 protocol 'icmp'", "set firewall ipv4 name V4-INGRESS rule 102 description 'Rule 102 is configured by Ansible RM'", "set firewall ipv4 name V4-INGRESS rule 102", "set firewall ipv6 name V6-INGRESS description 'This rule-set is configured by Ansible RM'", "set firewall ipv6 name EGRESS description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_02(self): """Test if plugin correctly replaces a particular rule(s) and rule set attribute(s) - without affecting the others + without affecting the others """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=False, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", fragment="match-frag", disable=True, ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), ], ), ], state="replaced", ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", "delete firewall ipv4 name V4-INGRESS default-log", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101 action 'accept'", "set firewall ipv4 name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv4 name V4-INGRESS rule 101 disable", "set firewall ipv4 name V4-INGRESS rule 101 fragment 'match-frag'", "set firewall ipv4 name V4-INGRESS rule 101 ipsec 'match-ipsec'", "set firewall ipv4 name V4-INGRESS rule 101 protocol 'icmp'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=300)], protocol="icmp", disable=True, log="enable", - ) + ), + ], + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="105", + action="offload", + offload_target="test", + ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), dict( name="IF-TEST", rules=[ dict( number="10", action="accept", icmp=dict(type_name="echo-request"), state=dict(related=True), inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="V6-INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), ], ), ], state="replaced", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_rep_idem_02(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=300)], protocol="icmp", disable=True, log="enable", - ) + ), ], ), ], ), ], state="replaced", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_mer_idem_01(self): - """Test if plugin correctly has no effect if there is no change in the configuration - """ + """Test if plugin correctly has no effect if there is no change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=300)], protocol="icmp", disable=True, log="enable", - ) + ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), dict( name="IF-TEST", rules=[ dict( number="10", action="accept", icmp=dict(type_name="echo-request"), state=dict(related=True), inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="V6-INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), ], ), ], state="merged", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_01(self): - """Test if plugin correctly resets the entire rule set if there is a change in the configuration - """ + """Test if plugin correctly resets the entire rule set if there is a change in the configuration""" set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-IN", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="1", action="reject", description="Rule 1 is configured by Ansible RM", ipsec="match-ipsec", log="enable", protocol="tcp", fragment="match-frag", disable=False, source=dict( group=dict( address_group="IN-ADDR-GROUP", network_group="IN-NET-GROUP", port_group="IN-PORT-GROUP", ), ), ), dict( number="2", action="accept", description="Rule 102 is configured by Ansible RM", protocol="icmp", disable=True, ), ], ), dict( name="MULTIPLE-RULE", default_action="drop", rules=[ dict( number="1", action="accept", protocol="all", ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-IN", default_action="accept", description="This rule-set is configured by Ansible RM", ), dict( name="V6-EG", default_action="reject", description="This rule-set is configured by Ansible RM", ), ], ), ], state="overridden", ), ) commands = [ "delete firewall ipv6 name V6-INGRESS", "delete firewall ipv6 name EGRESS", "delete firewall ipv4 name V4-INGRESS", "delete firewall ipv4 name EGRESS", "delete firewall ipv4 name MULTIPLE-RULE", "delete firewall ipv4 input filter", "delete firewall ipv4 output filter", "delete firewall ipv6 input filter", "delete firewall ipv6 output filter", "delete firewall ipv4 name IF-TEST", + "delete firewall ipv4 name V4-OFFLOAD", "set firewall ipv4 name MULTIPLE-RULE default-action 'drop'", "set firewall ipv4 name MULTIPLE-RULE rule 1", "set firewall ipv4 name MULTIPLE-RULE rule 1 action 'accept'", "set firewall ipv4 name MULTIPLE-RULE rule 1 protocol 'all'", "set firewall ipv4 name V4-IN default-action 'accept'", "set firewall ipv4 name V4-IN description 'This is IPv4 INGRESS rule set'", "set firewall ipv4 name V4-IN default-log", "set firewall ipv4 name V4-IN rule 1 protocol 'tcp'", "set firewall ipv4 name V4-IN rule 1 log", "set firewall ipv4 name V4-IN rule 1 description 'Rule 1 is configured by Ansible RM'", "set firewall ipv4 name V4-IN rule 1 fragment 'match-frag'", "set firewall ipv4 name V4-IN rule 1 source group address-group IN-ADDR-GROUP", "set firewall ipv4 name V4-IN rule 1 source group network-group IN-NET-GROUP", "set firewall ipv4 name V4-IN rule 1 source group port-group IN-PORT-GROUP", "set firewall ipv4 name V4-IN rule 1", "set firewall ipv4 name V4-IN rule 1 action 'reject'", "set firewall ipv4 name V4-IN rule 1 ipsec 'match-ipsec'", "set firewall ipv4 name V4-IN rule 2 disable", "set firewall ipv4 name V4-IN rule 2 action 'accept'", "set firewall ipv4 name V4-IN rule 2 protocol 'icmp'", "set firewall ipv4 name V4-IN rule 2 description 'Rule 102 is configured by Ansible RM'", "set firewall ipv4 name V4-IN rule 2", "set firewall ipv6 name V6-IN default-action 'accept'", "set firewall ipv6 name V6-IN description 'This rule-set is configured by Ansible RM'", "set firewall ipv6 name V6-EG default-action 'reject'", "set firewall ipv6 name V6-EG description 'This rule-set is configured by Ansible RM'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_02(self): """Test that the plugin correctly resets the entire - rule sets configuration if changes are detected + rule sets configuration if changes are detected """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", protocol="udp", ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="EGRESS", default_action="reject", description="This rule-set is configured by Ansible RM", rules=[ dict( number="20", action="accept", protocol="udp", ), ], ), ], ), ], state="overridden", ), ) commands = [ "delete firewall ipv6 name V6-INGRESS", "delete firewall ipv6 name EGRESS", "delete firewall ipv4 name V4-INGRESS", "delete firewall ipv4 name EGRESS", "delete firewall ipv4 input filter", "delete firewall ipv4 output filter", "delete firewall ipv6 input filter", "delete firewall ipv6 output filter", "delete firewall ipv4 name IF-TEST", "delete firewall ipv4 name MULTIPLE-RULE", + "delete firewall ipv4 name V4-OFFLOAD", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS default-log", "set firewall ipv4 name V4-INGRESS description 'This is IPv4 INGRESS rule set'", "set firewall ipv4 name V4-INGRESS default-action 'accept'", "set firewall ipv4 name V4-INGRESS rule 101 protocol 'udp'", "set firewall ipv4 name V4-INGRESS rule 101 action 'accept'", "set firewall ipv6 name EGRESS description 'This rule-set is configured by Ansible RM'", "set firewall ipv6 name EGRESS default-action 'reject'", "set firewall ipv6 name EGRESS rule 20", "set firewall ipv6 name EGRESS rule 20 protocol 'udp'", - "set firewall ipv6 name EGRESS rule 20 action 'accept'" + "set firewall ipv6 name EGRESS rule 20 action 'accept'", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_v4v6_rule_sets_rule_ovr_idem_01(self): """Test that the plugin is idempotent in overridden state - if there are no changes to the rule sets + if there are no changes to the rule sets """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=300)], protocol="icmp", disable=True, log="enable", - ) + ), + ], + ), + dict( + name="V4-OFFLOAD", + rules=[ + dict( + number="105", + action="offload", + offload_target="test", + ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), dict( name="IF-TEST", rules=[ dict( number="10", action="accept", icmp=dict(type_name="echo-request"), state=dict(related=True), inbound_interface=dict(name="eth0"), outbound_interface=dict(group="the-ethers"), disable=True, - ) + ), ], ), dict( name="EGRESS", default_action="reject", ), dict( name="MULTIPLE-RULE", default_action="drop", rules=[ dict( number="1", action="accept", protocol="all", ), dict( number="2", action="drop", protocol="all", ), ], ), ], ), dict( afi="ipv6", rule_sets=[ dict( name="V6-INGRESS", default_action="accept", ), dict( name="EGRESS", default_action="reject", rules=[ dict( icmp=dict(type_name="echo-request"), number=20, ), ], ), dict( filter="input", rules=[ dict( number="1", action="jump", jump_target="V6-INGRESS", ), ], ), dict( filter="output", rules=[ dict( number="1", action="jump", jump_target="EGRESS", ), ], ), ], ), ], state="overridden", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_firewall_v6_rule_sets_rule_merged_01_version(self): - """Test if plugin correctly adds ipv6 rule set with rules - """ + """Test if plugin correctly adds ipv6 rule set with rules""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", disable=True, icmp=dict(type_name="echo-request"), log="enable", ), dict( number="102", action="reject", description="Rule 102 is configured by Ansible", protocol="ipv6-icmp", icmp=dict(type=7), ), ], ), ], ), ], state="merged", ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", "set firewall ipv6 name INBOUND description 'This is IPv6 INBOUND rule set'", "set firewall ipv6 name INBOUND default-log", "set firewall ipv6 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 disable", "set firewall ipv6 name INBOUND rule 101 action 'accept'", "set firewall ipv6 name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6 name INBOUND rule 101 icmpv6 type-name echo-request", "set firewall ipv6 name INBOUND rule 101 log", "set firewall ipv6 name INBOUND rule 102", "set firewall ipv6 name INBOUND rule 102 action 'reject'", "set firewall ipv6 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6 name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6 name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_jump_rules_merged_01(self): - """Test if plugin correctly adds rule set with a jump action - """ + """Test if plugin correctly adds rule set with a jump action""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set with a jump action", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="jump", description="Rule 101 is configured by Ansible", ipsec="match-ipsec", protocol="icmp", icmp=dict(type_name="echo-request"), jump_target="PROTECT-RE", - packet_length_exclude=[dict(length=100), dict(length=200)] + packet_length_exclude=[dict(length=100), dict(length=200)], ), dict( number="102", action="reject", description="Rule 102 is configured by Ansible", protocol="ipv6-icmp", icmp=dict(type=7), ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", "set firewall ipv6 name INBOUND description 'This is IPv6 INBOUND rule set with a jump action'", "set firewall ipv6 name INBOUND default-log", "set firewall ipv6 name INBOUND rule 101 protocol 'icmp'", "set firewall ipv6 name INBOUND rule 101 packet-length-exclude 100", "set firewall ipv6 name INBOUND rule 101 packet-length-exclude 200", "set firewall ipv6 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 ipsec 'match-ipsec'", "set firewall ipv6 name INBOUND rule 101 icmpv6 type-name echo-request", "set firewall ipv6 name INBOUND rule 101 action 'jump'", "set firewall ipv6 name INBOUND rule 101 jump-target 'PROTECT-RE'", "set firewall ipv6 name INBOUND rule 102", "set firewall ipv6 name INBOUND rule 102 action 'reject'", "set firewall ipv6 name INBOUND rule 102 description 'Rule 102 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 102 protocol 'ipv6-icmp'", - 'set firewall ipv6 name INBOUND rule 102 icmpv6 type 7', + "set firewall ipv6 name INBOUND rule 102 icmpv6 type 7", ] self.execute_module(changed=True, commands=commands) def test_vyos_firewall_log_merged_01(self): """Test if new stanza log is correctly applied""" set_module_args( dict( config=[ dict( afi="ipv6", rule_sets=[ dict( name="INBOUND", description="This is IPv6 INBOUND rule set with a log", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", log="enable", ), ], ), ], - ) + ), ], state="merged", - ) + ), ) commands = [ "set firewall ipv6 name INBOUND default-action 'accept'", "set firewall ipv6 name INBOUND description 'This is IPv6 INBOUND rule set with a log'", "set firewall ipv6 name INBOUND default-log", "set firewall ipv6 name INBOUND rule 101 log", "set firewall ipv6 name INBOUND rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv6 name INBOUND rule 101", "set firewall ipv6 name INBOUND rule 101 action 'accept'", ] self.maxDiff = None self.execute_module(changed=True, commands=commands) def test_vyos_firewall_log_replace_01(self): """Test that stanza is correctly replaced - without touching the other stanzas + without touching the other stanzas """ set_module_args( dict( config=[ dict( afi="ipv4", rule_sets=[ dict( name="V4-INGRESS", description="This is IPv4 V4-INGRESS rule set", default_action="accept", enable_default_log=True, rules=[ dict( number="101", action="accept", description="Rule 101 is configured by Ansible", packet_length_exclude=[dict(length=100), dict(length=200)], packet_length=[dict(length=22)], log="enable", ), ], ), ], - ) + ), ], state="replaced", - ) + ), ) commands = [ "delete firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101", "set firewall ipv4 name V4-INGRESS rule 101 action 'accept'", "set firewall ipv4 name V4-INGRESS rule 101 description 'Rule 101 is configured by Ansible'", "set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 100", "set firewall ipv4 name V4-INGRESS rule 101 packet-length-exclude 200", "set firewall ipv4 name V4-INGRESS rule 101 packet-length 22", "set firewall ipv4 name V4-INGRESS rule 101 log", ] self.maxDiff = None self.execute_module(changed=True, commands=commands)