Repository: ansible-collections/community.general

Issue:
nmcli: add ipv4.routing-rules, ipv4.may-fail options and disabled to method6
### Summary

I am trying to configure our network setup which is possible with nmcli command line but there are some missing options in the ansible collections which prevents us from using it. Adding this options will extend the possible configuration that the collection can do and get closer to nmcli command capabilities.



### Issue Type

Feature Idea

### Component Name

nmcli

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
This type of configuration is for example used for Lustre and Multi-Rail environments.
https://wiki.lustre.org/LNet_Router_Config_Guide#ARP_flux_issue_for_MR_node
```
nmcli c add type infiniband con-name ib0 \
  ifname ib0 infiniband.mtu 2044 ipv4.method manual \
  ipv4.may-fail false ipv4.addresses 10.0.0.3/16 \
  ipv4.route-metric 0 ipv4.routes "10.0.0.0/16 src=10.0.0.3 table=200" \
  ipv4.routing-rules "priority 0 from 10.0.0.3 lookup 200" \
  ipv6.method disabled connection.zone internal
```

```yaml (paste below)
vars:
  infiniband_interfaces:
    - device: ib0
      type: infiniband
      ip4: 10.0.0.3/16
      mtu: 2044
      route_table: 200
      may_fail4: false

tasks:
  - name: "Configure IB MR interface"
    community.general.nmcli:
       conn_name: "{{ item.device }}"
       ifname: "{{ item.device }}"
       type: "{{ item.type }}"
       ip4: "{{ item.ip4 }}"
       mtu: "{{ item.mtu }}"
       method4: manual
       method6: disabled
       routes4: "{{ item.ip4 | ipaddr('network') }}/{{ item.ip4 | ipaddr('prefix') }} src={{ item.ip4 | ipaddr('address') }} table={{ item.route_table }}"
       route_metric4: 0
       routing_rules4: "priority 0 from {{ item.ip4 | ipaddr('address') }} lookup {{ item.route_table }}"
       may_fail4: false
       state: present

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`lib/ansible/plugins/connection`](https://github.com/ansible-collections/community.general/blob/master/lib/ansible/plugins/connection)
* [`lib/ansible/plugins/lookup`](https://github.com/ansible-collections/community.general/blob/master/lib/ansible/plugins/lookup)
* [`plugins/module_utils/mh/mixins/vars.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/module_utils/mh/mixins/vars.py)
* [`plugins/modules/net_tools/infinity/infinity.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/net_tools/infinity/infinity.py)
* [`plugins/modules/net_tools/nmcli.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/net_tools/nmcli.py)
* [`plugins/modules/system/interfaces_file.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/system/interfaces_file.py)
* [`tests/integration/targets/aix_devices/tasks/main.yml`](https://github.com/ansible-collections/community.general/blob/master/tests/integration/targets/aix_devices/tasks/main.yml)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @MeganLiu @MorrisA @alcamie101 @bcoca @d-little @flynn1973 @gforster @hryamzik @kairoaraujo @marvin-sinister @mator @molekuul @nerzhul @obourdon @ramooncamacho @russoz @wtcross
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
!component =plugins/modules/net_tools/nmcli.py
Files identified in the description:
* [`plugins/modules/net_tools/nmcli.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/net_tools/nmcli.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/plugins/modules/net_tools/nmcli.py b/plugins/modules/net_tools/nmcli.py
--- a/plugins/modules/net_tools/nmcli.py
+++ b/plugins/modules/net_tools/nmcli.py
@@ -95,6 +95,11 @@
             - Set metric level of ipv4 routes configured on interface.
         type: int
         version_added: 2.0.0
+    routing_rules4:
+        description:
+            - Is the same as in an C(ip route add) command, except always requires specifying a priority.
+        type: str
+        version_added: 3.3.0
     never_default4:
         description:
             - Set as default route.
@@ -126,6 +131,12 @@
         type: str
         choices: [auto, link-local, manual, shared, disabled]
         version_added: 2.2.0
+    may_fail4:
+        description:
+            - If you need I(ip4) configured before C(network-online.target) is reached, set this option to C(false).
+        type: bool
+        default: true
+        version_added: 3.3.0
     ip6:
         description:
             - The IPv6 address to this interface.
@@ -164,8 +175,9 @@
         description:
             - Configuration method to be used for IPv6
             - If I(ip6) is set, C(ipv6.method) is automatically set to C(manual) and this parameter is not needed.
+            - C(disabled) was added in community.general 3.3.0.
         type: str
-        choices: [ignore, auto, dhcp, link-local, manual, shared]
+        choices: [ignore, auto, dhcp, link-local, manual, shared, disabled]
         version_added: 2.2.0
     mtu:
         description:
@@ -675,11 +687,13 @@ def __init__(self, module):
         self.gw4_ignore_auto = module.params['gw4_ignore_auto']
         self.routes4 = module.params['routes4']
         self.route_metric4 = module.params['route_metric4']
+        self.routing_rules4 = module.params['routing_rules4']
         self.never_default4 = module.params['never_default4']
         self.dns4 = module.params['dns4']
         self.dns4_search = module.params['dns4_search']
         self.dns4_ignore_auto = module.params['dns4_ignore_auto']
         self.method4 = module.params['method4']
+        self.may_fail4 = module.params['may_fail4']
         self.ip6 = module.params['ip6']
         self.gw6 = module.params['gw6']
         self.gw6_ignore_auto = module.params['gw6_ignore_auto']
@@ -762,8 +776,10 @@ def connection_options(self, detect_change=False):
                 'ipv4.ignore-auto-routes': self.gw4_ignore_auto,
                 'ipv4.routes': self.routes4,
                 'ipv4.route-metric': self.route_metric4,
+                'ipv4.routing-rules': self.routing_rules4,
                 'ipv4.never-default': self.never_default4,
                 'ipv4.method': self.ipv4_method,
+                'ipv4.may-fail': self.may_fail4,
                 'ipv6.addresses': self.ip6,
                 'ipv6.dns': self.dns6,
                 'ipv6.dns-search': self.dns6_search,
@@ -935,6 +951,7 @@ def settings_type(setting):
                        'ipv4.never-default',
                        'ipv4.ignore-auto-dns',
                        'ipv4.ignore-auto-routes',
+                       'ipv4.may-fail',
                        'ipv6.ignore-auto-dns',
                        'ipv6.ignore-auto-routes'):
             return bool
@@ -1155,11 +1172,13 @@ def main():
             gw4_ignore_auto=dict(type='bool', default=False),
             routes4=dict(type='list', elements='str'),
             route_metric4=dict(type='int'),
+            routing_rules4=dict(type='str'),
             never_default4=dict(type='bool', default=False),
             dns4=dict(type='list', elements='str'),
             dns4_search=dict(type='list', elements='str'),
             dns4_ignore_auto=dict(type='bool', default=False),
             method4=dict(type='str', choices=['auto', 'link-local', 'manual', 'shared', 'disabled']),
+            may_fail4=dict(type='bool', default=True),
             dhcp_client_id=dict(type='str'),
             ip6=dict(type='str'),
             gw6=dict(type='str'),
@@ -1167,7 +1186,7 @@ def main():
             dns6=dict(type='list', elements='str'),
             dns6_search=dict(type='list', elements='str'),
             dns6_ignore_auto=dict(type='bool', default=False),
-            method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared']),
+            method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared', 'disabled']),
             # Bond Specific vars
             mode=dict(type='str', default='balance-rr',
                       choices=['802.3ad', 'active-backup', 'balance-alb', 'balance-rr', 'balance-tlb', 'balance-xor', 'broadcast']),


Test patch:
diff --git a/tests/unit/plugins/modules/net_tools/test_nmcli.py b/tests/unit/plugins/modules/net_tools/test_nmcli.py
--- a/tests/unit/plugins/modules/net_tools/test_nmcli.py
+++ b/tests/unit/plugins/modules/net_tools/test_nmcli.py
@@ -98,6 +98,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -128,6 +129,7 @@
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
 ipv4.dns-search:                        search.redhat.com
+ipv4.may-fail:                          yes
 ipv6.dns-search:                        search6.redhat.com
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
@@ -158,6 +160,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -187,6 +190,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -218,6 +222,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -275,6 +280,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -370,6 +376,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no
 ipv6.ignore-auto-routes:                no
@@ -399,6 +406,7 @@
 ipv4.ignore-auto-dns:                   no
 ipv4.ignore-auto-routes:                no
 ipv4.never-default:                     no
+ipv4.may-fail:                          yes
 ipv4.dns:                               1.1.1.1,8.8.8.8
 ipv6.method:                            auto
 ipv6.ignore-auto-dns:                   no