Compare commits

...

75 commits
3.0.0 ... main

Author SHA1 Message Date
Felix Fontein
8edc8018a7
Ansible-core devel EE: use Python 3.12. (#387)
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-07-31 07:18:42 +02:00
Felix Fontein
e78df4a4cf Prepare 3.9.0.
Some checks failed
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-07-28 22:35:44 +02:00
Michael Jenny
c9d15bc43a
OVPN server accepts now multiple items (RouterOS >= 7.17) (#383)
* Since RouterOS 7.17 VRF is supported for OVPN server. It now supports
multiple entries and single-value is now obsolete. This demands for a
versioned API.

* Add change fragment

* Add change fragment

* add license field

* Update changelogs/fragments/385-vrf-support-for-ovpn-server

Co-authored-by: Felix Fontein <felix@fontein.de>

* rename

* remove license field

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2025-07-28 22:34:15 +02:00
Tr4sK
008b5f893a
Update logging action fields for ROS 7.18 (#381)
* Update logging action fields for ROS 7.18

* Add changelog fragment

* Update changelogs/fragments/381-logging-cef.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2025-07-28 22:33:40 +02:00
hansmi
b70b4a72b3
Set mangle passthrough default for RouterOS 7.19 (#382)
The behaviour of the `passthrough` property in `ip/firewall/mangle` has
changed in RouterOS 7.19:

```
*) firewall - always show "passthrough" when exporting mangle table;
```

Per the documentation at [1] the default is `true`.

[1] https://help.mikrotik.com/docs/spaces/ROS/pages/48660587/Mangle
2025-07-28 22:33:24 +02:00
hansmi
1f38be9e56
Update IPv6 settings fields for RouterOS 7.17/7.18 (#380)
From the RouterOS 7.17 changelog:

*) ipv6 - added IPv6 settings related to stale IPv6 neighbor cleanup;
*) ipv6 - added support for manual link-local address configuration;

And from the RouterOS 7.18 changelog:

*) ipv6 - added FastTrack support;
*) ipv6 - added support for neighbor removal and static entries;

The change also removes the default value for the `max-neighbor-entries`
field. It's documented to vary depending on the RAM size.
2025-07-28 22:33:11 +02:00
Felix Fontein
e988b18acf Normalize changelog configs.
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-07-27 16:35:29 +02:00
Felix Fontein
6e9d2e1379 Release 3.8.1.
Some checks are pending
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
Collection Docs / Build Ansible Docs (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-07-26 14:38:22 +02:00
Felix Fontein
1c182725ce
Prevent deprecation warnings when using ansible-core 2.19. (#385)
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-07-25 22:26:39 +02:00
Felix Fontein
9099fcd698 Prepare 3.8.1. 2025-07-25 22:19:29 +02:00
Felix Fontein
852e21a2f2
Move EE tests to antsibull-nox. (#384)
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-07-25 18:59:28 +02:00
Felix Fontein
bb7eadbc9f Avoid tabs.
Some checks failed
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-07-06 18:05:28 +02:00
Felix Fontein
d9be02bdb8 Adjust README.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-07-01 22:34:37 +02:00
Felix Fontein
3475751b30
Add stable-2.19 to CI; add ignore-2.20.txt (#378)
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Waiting to run
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
* Add ignore-2.20.txt.

* Restrict stable-2.19 versions.
2025-07-01 07:24:51 +02:00
Felix Fontein
6008397375 Add linting check for RST code blocks.
Some checks failed
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-06-18 21:48:53 +02:00
Felix Fontein
b751d79a98 Update yamllint config, fix YAML.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-06-17 07:19:14 +02:00
Felix Fontein
aa83116c78 Release 3.8.0.
Some checks failed
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-06-14 16:49:40 +02:00
Felix Fontein
4571d777de Prepare 3.8.0.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-06-13 08:11:36 +02:00
Michael Jenny
c7b2275f2c
Support RouterOS path /interface ethernet switch port-isolation and /routing bfd configuration (#375)
* Update _api_data.py

Add /interface/bridge property "port-cost-mode" which is supported since RouterOS 7.13.

See changelog for details: https://mikrotik.com/download/changelogs

* Create 371-add-bridge-port-cost-mode.yml

* Update changelogs/fragments/371-add-bridge-port-cost-mode.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Support for /interface ethernet switch port-isolation
  RouterOS has supported the /interface ethernet switch port-isolation path since version 6.43.
  This feature enables hardware offloaded Layer‑2 port isolation on supported switch chips

Support for /routing bfd configuration
  Since RouterOS 7.11 it supports stable bfd support.
  See: https://iparchitechs.com/mikrotik-routeros-v7-11-stable-released/

* .

* Add change file

* attr order

* Add name to fields

* PEP8

* Update changelogs/fragments/375-port_isolation-and-routing_bfd_configuration.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/375-port_isolation-and-routing_bfd_configuration.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Michael Jenny <mjenny@ansible.zvcloud.de>
2025-06-13 08:10:37 +02:00
SdVSysAdmin
49e4b83594
support for 'ip ipsec mode-config' (#376)
* Added support for 'ip ipsec mode-config'

* Create 376-ipsec-mode-config.yml

---------

Co-authored-by: Laurent Papier <papier@sdv.fr>
2025-06-13 08:10:27 +02:00
Felix Fontein
88806047e3 Enable no-trailing-whitespace test.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-06-04 15:19:28 +02:00
Felix Fontein
221a697af9 Release 3.7.0.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-05-31 16:57:51 +02:00
Felix Fontein
ab1026504c
Allow to ignore dynamic and builtin entries. (#373) 2025-05-31 16:50:52 +02:00
Felix Fontein
bfd6b0bb13 Prepare 3.7.0.
Some checks failed
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-05-29 14:59:45 +02:00
Michael Jenny
f5b952751e
Update _api_data.py (#371)
* Update _api_data.py

Add /interface/bridge property "port-cost-mode" which is supported since RouterOS 7.13.

See changelog for details: https://mikrotik.com/download/changelogs

* Create 371-add-bridge-port-cost-mode.yml

* Update changelogs/fragments/371-add-bridge-port-cost-mode.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2025-05-29 14:58:38 +02:00
Felix Fontein
5b81c157fe Add reformat commit to .git-blame-ignore-revs.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-04-26 12:22:44 +02:00
Felix Fontein
08152376de Fix linting errors. 2025-04-26 12:18:29 +02:00
Felix Fontein
3af45c33f1 Add yamllint to antsibull-nox and add config files. 2025-04-26 12:18:29 +02:00
Felix Fontein
e52978b6d2 Fix info on blanket license statement for changelog fragments.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-04-24 22:48:23 +02:00
Felix Fontein
d1db4bec92
Adjust EE tests. (#366)
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Waiting to run
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-04-24 20:38:56 +02:00
Felix Fontein
180e87fd5d Release 3.6.0.
Some checks failed
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-04-21 13:40:42 +02:00
Felix Fontein
81237dbde4 Prepare 3.6.0.
Some checks are pending
Collection Docs / Build Ansible Docs (push) Waiting to run
Collection Docs / Publish Ansible Docs (push) Blocked by required conditions
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Waiting to run
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
nox / ansible-test (push) Waiting to run
2025-04-20 21:49:40 +02:00
hansmi
770e4d2c8d
Rename Wireguard peer "is-responder" to "responder" for RouterOS 7.17 (#364)
RouterOS 7.17 renamed the `is-responder` property in
`/interface/wireguard/peers` to `responder`.
2025-04-20 21:48:26 +02:00
Felix Fontein
4b9925ac23 Re-add reuse check.
Some checks failed
Collection Docs / Build Ansible Docs (push) Has been cancelled
execution environment / Build and test EE (ansible-core devel @ RHEL UBI 9) (push) Has been cancelled
execution environment / Build and test EE (ansible-core 2.15 @ Rocky Linux 9) (push) Has been cancelled
nox / Run extra sanity tests (push) Has been cancelled
nox / ansible-test (push) Has been cancelled
Collection Docs / Publish Ansible Docs (push) Has been cancelled
2025-04-19 13:11:42 +02:00
Felix Fontein
e286d768c0
Use antsibull-nox for CI. (#361) 2025-04-19 13:07:53 +02:00
Tim de Boer
9dba8082f9
Field name change in 'routing bgp connection' (from 'address-families' to 'afi') (#360)
* Field name change in 'routing bgp connection' (from 'address-families' to 'afi')

* Field name change in 'routing bgp connection' (from 'address-families' to 'afi')
2025-04-10 13:03:08 +02:00
Felix Fontein
3a34752296 Migrate .reuse/dep5 to REUSE.toml. 2025-03-29 12:17:25 +01:00
hansmi
a920caa16a
Add "mdns-repeat-ifaces" field to "ip dns" (#358)
RouterOS 7.16 introduced an mDNS proxy, configurable via
`mdns-repeat-ifaces` under `/ip/dns`.
2025-03-26 06:51:10 +01:00
Felix Fontein
9d382a1b10 Release 3.5.0. 2025-03-22 12:39:33 +01:00
Felix Fontein
2b1be7f011 Prepare 3.5.0. 2025-03-21 22:07:58 +01:00
hansmi
8736996317
Change default for ddns-enabled to "auto" for RouterOS 7.17 and newer (#350)
From the RouterOS 7.17 changelog:

> *) cloud - changed ddns-enabled setting from "no" to "auto" (service
> is enabled when BTH is enabled);

`no` is not supported anymore, only `yes` and `auto` are.
2025-03-13 07:08:14 +01:00
Felix Fontein
30a79061f3
Use shared unit test utils from community.internal_test_tools (#353)
* Use shared unit test utils from community.internal_test_tools.

* Make sure community.internal_test_tools is installed in CI.
2025-03-12 21:34:12 +01:00
hansmi
ab446b4449
Fix typo in "API" (#351) 2025-03-12 20:06:39 +01:00
Felix Fontein
be9a7ed3ad
Improve tests (#348)
* Simplify filter error tests.

* Convert set_module_args to context manager.
2025-03-07 20:45:39 +01:00
Felix Fontein
6aaead1d4a Group CI updates. 2025-03-03 19:00:16 +01:00
Felix Fontein
ffc928242b Release 3.4.0. 2025-02-24 06:33:52 +01:00
Felix Fontein
f54244b7d0 Prepare 3.4.0. 2025-02-24 06:19:51 +01:00
Felix Fontein
3ba33ccd99
Remove primary key. (#345) 2025-02-24 06:16:06 +01:00
Tim de Boer
e302fed6cf
Added support for 'ip dns forwarders' (#343) 2025-02-18 21:25:40 +01:00
Felix Fontein
9e4b6c197d Remove outdated comments. 2025-02-14 23:26:11 +01:00
Felix Fontein
a9f787fd76 Clean up workflow. 2025-02-11 22:50:43 +01:00
Felix Fontein
f6d50f8cc5 Release 3.3.0. 2025-01-27 05:32:10 +01:00
Peter Petrovich
388366542d
Add 'interface 6to4' path, allows manage 6to4 tunnels like HE (#342) 2025-01-26 12:52:36 +01:00
Peter Petrovich
575af30d88
DHCPv6 client: add new options from 7.15+ (#341)
* script
* custom-duid
* use-interface-duid
* validate-server-duid
2025-01-26 12:34:43 +01:00
Johannes Münch
85d24d180e
Fixes #284: Implement 'interface wireless access-list' and 'interface wireless connect-list' (#340)
Add changelog fragment
2025-01-24 21:40:39 +01:00
Felix Fontein
11454b802e Prepare 3.3.0. 2025-01-21 21:03:08 +01:00
Johannes Münch
364ef6c5fe
Fixes #338: Add require-message-auth in radius path (#339)
* Fixes #338: Add require-message-auth in radius path

Add changelog fragment

* Update changelogs/fragments/339-add-require-message-auth-for-radius.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2025-01-21 20:48:38 +01:00
Felix Fontein
1466c9f984 Fix CI badge image URL. Add documentation badge. 2025-01-04 11:25:03 +01:00
Felix Fontein
dcdca90dd0 Add extra sanity test for routeros action group. 2025-01-03 14:49:49 +01:00
Felix Fontein
4241179471 Release 3.2.0. 2024-12-30 22:03:45 +01:00
Felix Fontein
71882863a5 Prepare 3.2.0 release. 2024-12-30 21:13:39 +01:00
Felix Fontein
44e6bb6f7a
Add 'idempotent' attribute (#337)
* Add 'idempotent' attribute.

* Mention check mode in attribute description.

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
2024-12-30 21:11:17 +01:00
Felix Fontein
2a3460827d Fix doc fragments indents. 2024-12-29 15:48:00 +01:00
Felix Fontein
1e0c582b98 Improve formulations. 2024-12-28 16:52:27 +01:00
Felix Fontein
539119c57d
Reformat documentation with 'andebox yaml-doc' (#335)
* Reformat documentation with 'andebox yaml-doc'.

* Revert unwanted changes.
2024-12-28 15:34:20 +01:00
Felix Fontein
77de6d90bf Improve language. 2024-12-28 14:27:15 +01:00
Felix Fontein
995ab18e7b
Fix some issues pointed out by zizmor. (#333) 2024-12-14 14:56:14 +01:00
Serhiy
a7340eae1a
community-list for /routing/filter (#331)
* Update _api_data.py

added ('routing', 'filter', 'community-list')

* Update api_info.py

added routing filter community-list

* Update api_modify.py

added routing filter community-list

* Create 331-add-routing-filter-community-list.yml

* Update api_info.py

* Update api_modify.py
2024-12-14 14:20:58 +01:00
Felix Fontein
0bf4b3ef8c Release 3.1.0. 2024-12-02 20:45:03 +01:00
Felix Fontein
c3e57efa9d Forgot to bump version. 2024-12-02 20:44:15 +01:00
Felix Fontein
8dbad9a8d4 Prepare 3.1.0. 2024-12-02 20:39:54 +01:00
Felix Fontein
c27c1906aa Re-classify change as feature. 2024-12-02 20:39:48 +01:00
liquorice-head
249b1a92e2
Update _api_data.py (#324)
* Update _api_data.py

* Create 324-fix-firewall-log-and-log-prefix.yaml

* Update changelogs/fragments/324-fix-firewall-log-and-log-prefix.yaml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update test_api_info.py

* fix tests

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: alsmirnov <alsmirnov@ourgapps.com>
2024-11-23 00:19:05 +01:00
dependabot[bot]
14d89a3cfa
Bump fsfe/reuse-action from 4 to 5 (#328)
Bumps [fsfe/reuse-action](https://github.com/fsfe/reuse-action) from 4 to 5.
- [Release notes](https://github.com/fsfe/reuse-action/releases)
- [Commits](https://github.com/fsfe/reuse-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: fsfe/reuse-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 06:41:47 +01:00
gardar
d44262d820
fix: add missing fields to ip pool path (#327)
* fix: add missing fields to `ip pool` path

Signed-off-by: gardar <gardar@users.noreply.github.com>

* fix: remove unneeded copy-from

Signed-off-by: gardar <gardar@users.noreply.github.com>

---------

Signed-off-by: gardar <gardar@users.noreply.github.com>
2024-11-04 19:05:52 +01:00
92 changed files with 2542 additions and 2190 deletions

6
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Reformat YAML: https://github.com/ansible-collections/community.routeros/pull/369
08152376de116e7d933d19ee25318f7a2eb222ae

View file

@ -9,3 +9,7 @@ updates:
directory: "/"
schedule:
interval: "weekly"
groups:
ci:
patterns:
- "*"

View file

@ -1,146 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# For the comprehensive list of the inputs supported by the ansible-community/ansible-test-gh-action GitHub Action, see
# https://github.com/marketplace/actions/ansible-test
name: CI
on:
# Run CI against all pushes (direct commits, also merged PRs), Pull Requests
push:
branches:
- main
- stable-*
pull_request:
# Run CI once per day (at 05:15 UTC)
schedule:
- cron: '15 5 * * *'
jobs:
sanity:
name: Sanity (Ⓐ${{ matrix.ansible }})
strategy:
matrix:
ansible:
# It's important that Sanity is tested against all stable-X.Y branches
# Testing against `devel` may fail as new tests are added.
- stable-2.15
- stable-2.16
- stable-2.17
- stable-2.18
- devel
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["stable-2.9", "stable-2.10", "stable-2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
steps:
- name: Perform sanity testing
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }}
ansible-core-version: ${{ matrix.ansible }}
codecov-token: ${{ secrets.CODECOV_TOKEN }}
testing-type: sanity
test-deps: >-
git+https://github.com/ansible-collections/ansible.utils.git,main
git+https://github.com/ansible-collections/ansible.netcommon.git,main
units:
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["stable-2.9", "stable-2.10", "stable-2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
name: Units (Ⓐ${{ matrix.ansible }})
strategy:
# As soon as the first unit test fails, cancel the others to free up the CI queue
fail-fast: true
matrix:
ansible:
- stable-2.16
- stable-2.17
- stable-2.18
- devel
steps:
- name: >-
Perform unit testing against
Ansible version ${{ matrix.ansible }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }}
ansible-core-version: ${{ matrix.ansible }}
codecov-token: ${{ secrets.CODECOV_TOKEN }}
testing-type: units
test-deps: >-
git+https://github.com/ansible-collections/ansible.utils.git,main
git+https://github.com/ansible-collections/ansible.netcommon.git,main
integration:
# Ansible-test on various stable branches does not yet work well with cgroups v2.
# Since ubuntu-latest now uses Ubuntu 22.04, we need to fall back to the ubuntu-20.04
# image for these stable branches. The list of branches where this is necessary will
# shrink over time, check out https://github.com/ansible-collections/news-for-maintainers/issues/28
# for the latest list.
runs-on: >-
${{ contains(fromJson(
'["stable-2.9", "stable-2.10", "stable-2.11"]'
), matrix.ansible) && 'ubuntu-20.04' || 'ubuntu-latest' }}
name: I (Ⓐ${{ matrix.ansible }}+py${{ matrix.python }})
strategy:
fail-fast: false
matrix:
ansible:
- devel
python:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
include:
# 2.15
- ansible: stable-2.15
python: "2.7"
- ansible: stable-2.15
python: "3.6"
- ansible: stable-2.15
python: "3.7"
# 2.16
- ansible: stable-2.16
python: "3.10"
# 2.17
- ansible: stable-2.17
python: "3.8"
# 2.18
- ansible: stable-2.18
python: "3.9"
steps:
- name: >-
Perform integration testing against
Ansible version ${{ matrix.ansible }}
under Python ${{ matrix.python }}
uses: felixfontein/ansible-test-gh-action@main
with:
ansible-core-github-repository-slug: ${{ contains(fromJson('["stable-2.9", "stable-2.10", "stable-2.11"]'), matrix.ansible) && 'ansible-community/eol-ansible' || 'ansible/ansible' }}
ansible-core-version: ${{ matrix.ansible }}
codecov-token: ${{ secrets.CODECOV_TOKEN }}
integration-continue-on-error: 'false'
integration-diff: 'false'
integration-retry-on-error: 'true'
test-deps: >-
git+https://github.com/ansible-collections/ansible.utils.git,main
git+https://github.com/ansible-collections/ansible.netcommon.git,main
target-python-version: ${{ matrix.python }}
testing-type: integration

View file

@ -7,7 +7,7 @@ name: Collection Docs
concurrency:
group: docs-pr-${{ github.head_ref }}
cancel-in-progress: true
on:
'on':
pull_request_target:
types: [opened, synchronize, reopened, closed]

View file

@ -7,7 +7,7 @@ name: Collection Docs
concurrency:
group: docs-push-${{ github.sha }}
cancel-in-progress: true
on:
'on':
push:
branches:
- main

View file

@ -1,158 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: execution environment
on:
# Run CI against all pushes (direct commits, also merged PRs), Pull Requests
push:
branches:
- main
- stable-*
pull_request:
# Run CI once per day (at 05:15 UTC)
# This ensures that even if there haven't been commits that we are still testing against latest version of ansible-builder
schedule:
- cron: '15 5 * * *'
env:
NAMESPACE: community
COLLECTION_NAME: routeros
jobs:
build:
name: Build and test EE (${{ matrix.name }})
strategy:
fail-fast: false
matrix:
name:
- ''
ansible_core:
- ''
ansible_runner:
- ''
base_image:
- ''
pre_base:
- ''
extra_vars:
- ''
other_deps:
- ''
exclude:
- ansible_core: ''
include:
- name: ansible-core devel @ RHEL UBI 9
ansible_core: https://github.com/ansible/ansible/archive/devel.tar.gz
ansible_runner: ansible-runner
other_deps: |2
python_interpreter:
package_system: python3.11 python3.11-pip python3.11-wheel python3.11-cryptography
python_path: "/usr/bin/python3.11"
base_image: docker.io/redhat/ubi9:latest
pre_base: '"#"'
- name: ansible-core 2.15 @ Rocky Linux 9
ansible_core: https://github.com/ansible/ansible/archive/stable-2.15.tar.gz
ansible_runner: ansible-runner
base_image: quay.io/rockylinux/rockylinux:9
pre_base: '"#"'
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
path: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ansible-builder and ansible-navigator
run: pip install ansible-builder ansible-navigator
- name: Verify requirements
run: ansible-builder introspect --sanitize .
- name: Make sure galaxy.yml has version entry
run: >-
python -c
'import yaml ;
f = open("galaxy.yml", "rb") ;
data = yaml.safe_load(f) ;
f.close() ;
data["version"] = data.get("version") or "0.0.1" ;
f = open("galaxy.yml", "wb") ;
f.write(yaml.dump(data).encode("utf-8")) ;
f.close() ;
'
working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}
- name: Build collection
run: |
ansible-galaxy collection build --output-path ../../../
working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}
- name: Create files for building execution environment
run: |
COLLECTION_FILENAME="$(ls "${{ env.NAMESPACE }}-${{ env.COLLECTION_NAME }}"-*.tar.gz)"
# EE config
cat > execution-environment.yml <<EOF
---
version: 3
dependencies:
ansible_core:
package_pip: ${{ matrix.ansible_core }}
ansible_runner:
package_pip: ${{ matrix.ansible_runner }}
galaxy: requirements.yml
${{ matrix.other_deps }}
images:
base_image:
name: ${{ matrix.base_image }}
additional_build_files:
- src: ${COLLECTION_FILENAME}
dest: src
additional_build_steps:
prepend_base:
- ${{ matrix.pre_base }}
EOF
echo "::group::execution-environment.yml"
cat execution-environment.yml
echo "::endgroup::"
# Requirements
cat > requirements.yml <<EOF
---
collections:
- name: src/${COLLECTION_FILENAME}
type: file
EOF
echo "::group::requirements.yml"
cat requirements.yml
echo "::endgroup::"
- name: Build image based on ${{ matrix.base_image }}
run: |
ansible-builder build --verbosity 3 --tag test-ee:latest --container-runtime docker
- name: Show images
run: docker image ls
- name: Run basic tests
run: >
ansible-navigator run
--mode stdout
--container-engine docker
--pull-policy never
--set-environment-variable ANSIBLE_PRIVATE_ROLE_VARS=true
--execution-environment-image test-ee:latest
-v
all.yml
${{ matrix.extra_vars }}
working-directory: ansible_collections/${{ env.NAMESPACE }}/${{ env.COLLECTION_NAME }}/tests/ee

View file

@ -1,51 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: extra-tests
on:
# Run CI against all pushes (direct commits, also merged PRs), Pull Requests
push:
branches:
- main
- stable-*
pull_request:
# Run CI once per day (at 05:15 UTC)
# This ensures that even if there haven't been commits that we are still testing against latest version of ansible-test for each ansible-base version
schedule:
- cron: '15 5 * * *'
env:
NAMESPACE: community
COLLECTION_NAME: routeros
jobs:
extra-sanity:
name: Extra Sanity
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
path: ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install ansible-core
run: pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
- name: Install collection dependencies
run: |
git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git ./ansible_collections/community/internal_test_tools
git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.netcommon.git ./ansible_collections/ansible/netcommon
git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.utils.git ./ansible_collections/ansible/utils
# NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429)
# run: ansible-galaxy collection install community.internal_test_tools ansible.netcommon -p .
- name: Run sanity tests
run: ../../community/internal_test_tools/tools/run.py --color
working-directory: ./ansible_collections/${{env.NAMESPACE}}/${{env.COLLECTION_NAME}}

View file

@ -1,20 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: import-galaxy
'on':
# Run CI against all pushes (direct commits, also merged PRs) to main, and all Pull Requests
push:
branches:
- main
- stable-*
pull_request:
jobs:
import-galaxy:
permissions:
contents: read
name: Test to import built collection artifact with Galaxy importer
uses: ansible-community/github-action-test-galaxy-import/.github/workflows/test-galaxy-import.yml@main

35
.github/workflows/nox.yml vendored Normal file
View file

@ -0,0 +1,35 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: nox
'on':
push:
branches:
- main
- stable-*
pull_request:
# Run CI once per day (at 05:15 UTC)
schedule:
- cron: '15 5 * * *'
workflow_dispatch:
jobs:
nox:
runs-on: ubuntu-latest
name: "Run extra sanity tests"
steps:
- name: Check out collection
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Run nox
uses: ansible-community/antsibull-nox@main
ansible-test:
uses: ansible-community/antsibull-nox/.github/workflows/reusable-nox-matrix.yml@main
with:
upload-codecov: true
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

View file

@ -1,31 +0,0 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
name: Verify REUSE
on:
push:
branches:
- main
- stable-*
pull_request:
branches:
- main
- stable-*
# Run CI once per day (at 05:15 UTC)
schedule:
- cron: '15 5 * * *'
jobs:
check:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: REUSE Compliance Check
uses: fsfe/reuse-action@v4

1
.gitignore vendored
View file

@ -4,6 +4,7 @@
/tests/output/
/changelogs/.plugin-cache.yaml
/tests/integration/inventory
# Byte-compiled / optimized / DLL files
__pycache__/

View file

@ -1,5 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Files: changelogs/fragments/*
Copyright: Ansible Project
License: GPL-3.0-or-later

53
.yamllint Normal file
View file

@ -0,0 +1,53 @@
---
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
extends: default
ignore: |
/changelogs/
rules:
line-length:
max: 300
level: error
document-start:
present: true
document-end: false
truthy:
level: error
allowed-values:
- 'true'
- 'false'
indentation:
spaces: 2
indent-sequences: true
key-duplicates: enable
trailing-spaces: enable
new-line-at-end-of-file: disable
hyphens:
max-spaces-after: 1
empty-lines:
max: 2
max-start: 0
max-end: 0
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
colons:
max-spaces-before: 0
max-spaces-after: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
braces:
min-spaces-inside: 0
max-spaces-inside: 1
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
comments:
min-spaces-from-content: 1
comments-indentation: false

54
.yamllint-docs Normal file
View file

@ -0,0 +1,54 @@
---
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
extends: default
ignore: |
/changelogs/
rules:
line-length:
max: 160
level: error
document-start:
present: false
document-end:
present: false
truthy:
level: error
allowed-values:
- 'true'
- 'false'
indentation:
spaces: 2
indent-sequences: true
key-duplicates: enable
trailing-spaces: enable
new-line-at-end-of-file: disable
hyphens:
max-spaces-after: 1
empty-lines:
max: 2
max-start: 0
max-end: 0
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
colons:
max-spaces-before: 0
max-spaces-after: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
braces:
min-spaces-inside: 0
max-spaces-inside: 1
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
comments:
min-spaces-from-content: 1
comments-indentation: false

54
.yamllint-examples Normal file
View file

@ -0,0 +1,54 @@
---
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
extends: default
ignore: |
/changelogs/
rules:
line-length:
max: 160
level: error
document-start:
present: true
document-end:
present: false
truthy:
level: error
allowed-values:
- 'true'
- 'false'
indentation:
spaces: 2
indent-sequences: true
key-duplicates: enable
trailing-spaces: enable
new-line-at-end-of-file: disable
hyphens:
max-spaces-after: 1
empty-lines:
max: 2
max-start: 0
max-end: 0
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
colons:
max-spaces-before: 0
max-spaces-after: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
braces:
min-spaces-inside: 0
max-spaces-inside: 1
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
comments:
min-spaces-from-content: 1
comments-indentation: false

53
.yamllint-extra-docs Normal file
View file

@ -0,0 +1,53 @@
---
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Felix Fontein <felix@fontein.de>
extends: default
ignore: |
/changelogs/
rules:
line-length:
max: 160
level: error
document-start: disable
document-end:
present: false
truthy:
level: error
allowed-values:
- 'true'
- 'false'
indentation:
spaces: 2
indent-sequences: true
key-duplicates: enable
trailing-spaces: enable
new-line-at-end-of-file: disable
hyphens:
max-spaces-after: 1
empty-lines:
max: 2
max-start: 0
max-end: 0
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
colons:
max-spaces-before: 0
max-spaces-after: 1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
braces:
min-spaces-inside: 0
max-spaces-inside: 1
octal-values:
forbid-implicit-octal: true
forbid-explicit-octal: true
comments:
min-spaces-from-content: 1
comments-indentation: false

View file

@ -2,132 +2,297 @@
**Topics**
- <a href="#v3-0-0">v3\.0\.0</a>
- <a href="#v3-8-1">v3\.8\.1</a>
- <a href="#release-summary">Release Summary</a>
- <a href="#bugfixes">Bugfixes</a>
- <a href="#v3-8-0">v3\.8\.0</a>
- <a href="#release-summary-1">Release Summary</a>
- <a href="#minor-changes">Minor Changes</a>
- <a href="#v3-7-0">v3\.7\.0</a>
- <a href="#release-summary-2">Release Summary</a>
- <a href="#minor-changes-1">Minor Changes</a>
- <a href="#v3-6-0">v3\.6\.0</a>
- <a href="#release-summary-3">Release Summary</a>
- <a href="#minor-changes-2">Minor Changes</a>
- <a href="#v3-5-0">v3\.5\.0</a>
- <a href="#release-summary-4">Release Summary</a>
- <a href="#minor-changes-3">Minor Changes</a>
- <a href="#v3-4-0">v3\.4\.0</a>
- <a href="#release-summary-5">Release Summary</a>
- <a href="#minor-changes-4">Minor Changes</a>
- <a href="#bugfixes-1">Bugfixes</a>
- <a href="#v3-3-0">v3\.3\.0</a>
- <a href="#release-summary-6">Release Summary</a>
- <a href="#minor-changes-5">Minor Changes</a>
- <a href="#v3-2-0">v3\.2\.0</a>
- <a href="#release-summary-7">Release Summary</a>
- <a href="#minor-changes-6">Minor Changes</a>
- <a href="#v3-1-0">v3\.1\.0</a>
- <a href="#release-summary-8">Release Summary</a>
- <a href="#minor-changes-7">Minor Changes</a>
- <a href="#bugfixes-2">Bugfixes</a>
- <a href="#v3-0-0">v3\.0\.0</a>
- <a href="#release-summary-9">Release Summary</a>
- <a href="#breaking-changes--porting-guide">Breaking Changes / Porting Guide</a>
- <a href="#removed-features-previously-deprecated">Removed Features \(previously deprecated\)</a>
- <a href="#v2-20-0">v2\.20\.0</a>
- <a href="#release-summary-1">Release Summary</a>
- <a href="#minor-changes">Minor Changes</a>
- <a href="#v2-19-0">v2\.19\.0</a>
- <a href="#release-summary-2">Release Summary</a>
- <a href="#minor-changes-1">Minor Changes</a>
- <a href="#v2-18-0">v2\.18\.0</a>
- <a href="#release-summary-3">Release Summary</a>
- <a href="#minor-changes-2">Minor Changes</a>
- <a href="#deprecated-features">Deprecated Features</a>
- <a href="#bugfixes">Bugfixes</a>
- <a href="#v2-17-0">v2\.17\.0</a>
- <a href="#release-summary-4">Release Summary</a>
- <a href="#minor-changes-3">Minor Changes</a>
- <a href="#v2-16-0">v2\.16\.0</a>
- <a href="#release-summary-5">Release Summary</a>
- <a href="#minor-changes-4">Minor Changes</a>
- <a href="#v2-15-0">v2\.15\.0</a>
- <a href="#release-summary-6">Release Summary</a>
- <a href="#minor-changes-5">Minor Changes</a>
- <a href="#v2-14-0">v2\.14\.0</a>
- <a href="#release-summary-7">Release Summary</a>
- <a href="#minor-changes-6">Minor Changes</a>
- <a href="#v2-13-0">v2\.13\.0</a>
- <a href="#release-summary-8">Release Summary</a>
- <a href="#minor-changes-7">Minor Changes</a>
- <a href="#bugfixes-1">Bugfixes</a>
- <a href="#v2-12-0">v2\.12\.0</a>
- <a href="#release-summary-9">Release Summary</a>
- <a href="#minor-changes-8">Minor Changes</a>
- <a href="#v2-11-0">v2\.11\.0</a>
- <a href="#release-summary-10">Release Summary</a>
- <a href="#minor-changes-9">Minor Changes</a>
- <a href="#v2-10-0">v2\.10\.0</a>
- <a href="#minor-changes-8">Minor Changes</a>
- <a href="#v2-19-0">v2\.19\.0</a>
- <a href="#release-summary-11">Release Summary</a>
- <a href="#minor-changes-10">Minor Changes</a>
- <a href="#bugfixes-2">Bugfixes</a>
- <a href="#v2-9-0">v2\.9\.0</a>
- <a href="#minor-changes-9">Minor Changes</a>
- <a href="#v2-18-0">v2\.18\.0</a>
- <a href="#release-summary-12">Release Summary</a>
- <a href="#minor-changes-11">Minor Changes</a>
- <a href="#minor-changes-10">Minor Changes</a>
- <a href="#deprecated-features">Deprecated Features</a>
- <a href="#bugfixes-3">Bugfixes</a>
- <a href="#v2-8-3">v2\.8\.3</a>
- <a href="#v2-17-0">v2\.17\.0</a>
- <a href="#release-summary-13">Release Summary</a>
- <a href="#minor-changes-11">Minor Changes</a>
- <a href="#v2-16-0">v2\.16\.0</a>
- <a href="#release-summary-14">Release Summary</a>
- <a href="#minor-changes-12">Minor Changes</a>
- <a href="#v2-15-0">v2\.15\.0</a>
- <a href="#release-summary-15">Release Summary</a>
- <a href="#minor-changes-13">Minor Changes</a>
- <a href="#v2-14-0">v2\.14\.0</a>
- <a href="#release-summary-16">Release Summary</a>
- <a href="#minor-changes-14">Minor Changes</a>
- <a href="#v2-13-0">v2\.13\.0</a>
- <a href="#release-summary-17">Release Summary</a>
- <a href="#minor-changes-15">Minor Changes</a>
- <a href="#bugfixes-4">Bugfixes</a>
- <a href="#v2-12-0">v2\.12\.0</a>
- <a href="#release-summary-18">Release Summary</a>
- <a href="#minor-changes-16">Minor Changes</a>
- <a href="#v2-11-0">v2\.11\.0</a>
- <a href="#release-summary-19">Release Summary</a>
- <a href="#minor-changes-17">Minor Changes</a>
- <a href="#v2-10-0">v2\.10\.0</a>
- <a href="#release-summary-20">Release Summary</a>
- <a href="#minor-changes-18">Minor Changes</a>
- <a href="#bugfixes-5">Bugfixes</a>
- <a href="#v2-9-0">v2\.9\.0</a>
- <a href="#release-summary-21">Release Summary</a>
- <a href="#minor-changes-19">Minor Changes</a>
- <a href="#bugfixes-6">Bugfixes</a>
- <a href="#v2-8-3">v2\.8\.3</a>
- <a href="#release-summary-22">Release Summary</a>
- <a href="#known-issues">Known Issues</a>
- <a href="#v2-8-2">v2\.8\.2</a>
- <a href="#release-summary-14">Release Summary</a>
- <a href="#bugfixes-4">Bugfixes</a>
- <a href="#v2-8-1">v2\.8\.1</a>
- <a href="#release-summary-15">Release Summary</a>
- <a href="#bugfixes-5">Bugfixes</a>
- <a href="#v2-8-0">v2\.8\.0</a>
- <a href="#release-summary-16">Release Summary</a>
- <a href="#minor-changes-12">Minor Changes</a>
- <a href="#bugfixes-6">Bugfixes</a>
- <a href="#v2-7-0">v2\.7\.0</a>
- <a href="#release-summary-17">Release Summary</a>
- <a href="#minor-changes-13">Minor Changes</a>
- <a href="#release-summary-23">Release Summary</a>
- <a href="#bugfixes-7">Bugfixes</a>
- <a href="#v2-6-0">v2\.6\.0</a>
- <a href="#release-summary-18">Release Summary</a>
- <a href="#minor-changes-14">Minor Changes</a>
- <a href="#v2-8-1">v2\.8\.1</a>
- <a href="#release-summary-24">Release Summary</a>
- <a href="#bugfixes-8">Bugfixes</a>
- <a href="#v2-5-0">v2\.5\.0</a>
- <a href="#release-summary-19">Release Summary</a>
- <a href="#minor-changes-15">Minor Changes</a>
- <a href="#v2-8-0">v2\.8\.0</a>
- <a href="#release-summary-25">Release Summary</a>
- <a href="#minor-changes-20">Minor Changes</a>
- <a href="#bugfixes-9">Bugfixes</a>
- <a href="#v2-4-0">v2\.4\.0</a>
- <a href="#release-summary-20">Release Summary</a>
- <a href="#minor-changes-16">Minor Changes</a>
- <a href="#v2-7-0">v2\.7\.0</a>
- <a href="#release-summary-26">Release Summary</a>
- <a href="#minor-changes-21">Minor Changes</a>
- <a href="#bugfixes-10">Bugfixes</a>
- <a href="#v2-6-0">v2\.6\.0</a>
- <a href="#release-summary-27">Release Summary</a>
- <a href="#minor-changes-22">Minor Changes</a>
- <a href="#bugfixes-11">Bugfixes</a>
- <a href="#v2-5-0">v2\.5\.0</a>
- <a href="#release-summary-28">Release Summary</a>
- <a href="#minor-changes-23">Minor Changes</a>
- <a href="#bugfixes-12">Bugfixes</a>
- <a href="#v2-4-0">v2\.4\.0</a>
- <a href="#release-summary-29">Release Summary</a>
- <a href="#minor-changes-24">Minor Changes</a>
- <a href="#bugfixes-13">Bugfixes</a>
- <a href="#known-issues-1">Known Issues</a>
- <a href="#v2-3-1">v2\.3\.1</a>
- <a href="#release-summary-21">Release Summary</a>
- <a href="#release-summary-30">Release Summary</a>
- <a href="#known-issues-2">Known Issues</a>
- <a href="#v2-3-0">v2\.3\.0</a>
- <a href="#release-summary-22">Release Summary</a>
- <a href="#minor-changes-17">Minor Changes</a>
- <a href="#bugfixes-11">Bugfixes</a>
- <a href="#release-summary-31">Release Summary</a>
- <a href="#minor-changes-25">Minor Changes</a>
- <a href="#bugfixes-14">Bugfixes</a>
- <a href="#v2-2-1">v2\.2\.1</a>
- <a href="#release-summary-23">Release Summary</a>
- <a href="#bugfixes-12">Bugfixes</a>
- <a href="#release-summary-32">Release Summary</a>
- <a href="#bugfixes-15">Bugfixes</a>
- <a href="#v2-2-0">v2\.2\.0</a>
- <a href="#release-summary-24">Release Summary</a>
- <a href="#minor-changes-18">Minor Changes</a>
- <a href="#bugfixes-13">Bugfixes</a>
- <a href="#release-summary-33">Release Summary</a>
- <a href="#minor-changes-26">Minor Changes</a>
- <a href="#bugfixes-16">Bugfixes</a>
- <a href="#new-modules">New Modules</a>
- <a href="#v2-1-0">v2\.1\.0</a>
- <a href="#release-summary-25">Release Summary</a>
- <a href="#minor-changes-19">Minor Changes</a>
- <a href="#bugfixes-14">Bugfixes</a>
- <a href="#release-summary-34">Release Summary</a>
- <a href="#minor-changes-27">Minor Changes</a>
- <a href="#bugfixes-17">Bugfixes</a>
- <a href="#new-modules-1">New Modules</a>
- <a href="#v2-0-0">v2\.0\.0</a>
- <a href="#release-summary-26">Release Summary</a>
- <a href="#minor-changes-20">Minor Changes</a>
- <a href="#release-summary-35">Release Summary</a>
- <a href="#minor-changes-28">Minor Changes</a>
- <a href="#breaking-changes--porting-guide-1">Breaking Changes / Porting Guide</a>
- <a href="#bugfixes-15">Bugfixes</a>
- <a href="#bugfixes-18">Bugfixes</a>
- <a href="#new-plugins">New Plugins</a>
- <a href="#filter">Filter</a>
- <a href="#v1-2-0">v1\.2\.0</a>
- <a href="#release-summary-27">Release Summary</a>
- <a href="#minor-changes-21">Minor Changes</a>
- <a href="#bugfixes-16">Bugfixes</a>
- <a href="#v1-1-0">v1\.1\.0</a>
- <a href="#release-summary-28">Release Summary</a>
- <a href="#minor-changes-22">Minor Changes</a>
- <a href="#v1-0-1">v1\.0\.1</a>
- <a href="#release-summary-29">Release Summary</a>
- <a href="#bugfixes-17">Bugfixes</a>
- <a href="#v1-0-0">v1\.0\.0</a>
- <a href="#release-summary-30">Release Summary</a>
- <a href="#bugfixes-18">Bugfixes</a>
- <a href="#v0-1-1">v0\.1\.1</a>
- <a href="#release-summary-31">Release Summary</a>
- <a href="#release-summary-36">Release Summary</a>
- <a href="#minor-changes-29">Minor Changes</a>
- <a href="#bugfixes-19">Bugfixes</a>
- <a href="#v1-1-0">v1\.1\.0</a>
- <a href="#release-summary-37">Release Summary</a>
- <a href="#minor-changes-30">Minor Changes</a>
- <a href="#v1-0-1">v1\.0\.1</a>
- <a href="#release-summary-38">Release Summary</a>
- <a href="#bugfixes-20">Bugfixes</a>
- <a href="#v1-0-0">v1\.0\.0</a>
- <a href="#release-summary-39">Release Summary</a>
- <a href="#bugfixes-21">Bugfixes</a>
- <a href="#v0-1-1">v0\.1\.1</a>
- <a href="#release-summary-40">Release Summary</a>
- <a href="#bugfixes-22">Bugfixes</a>
- <a href="#v0-1-0">v0\.1\.0</a>
- <a href="#release-summary-32">Release Summary</a>
- <a href="#minor-changes-23">Minor Changes</a>
- <a href="#release-summary-41">Release Summary</a>
- <a href="#minor-changes-31">Minor Changes</a>
<a id="v3-8-1"></a>
## v3\.8\.1
<a id="release-summary"></a>
### Release Summary
Bugfix release\.
<a id="bugfixes"></a>
### Bugfixes
* facts and api\_facts modules \- prevent deprecation warnings when used with ansible\-core 2\.19 \([https\://github\.com/ansible\-collections/community\.routeros/pull/384](https\://github\.com/ansible\-collections/community\.routeros/pull/384)\)\.
<a id="v3-8-0"></a>
## v3\.8\.0
<a id="release-summary-1"></a>
### Release Summary
Feature release\.
<a id="minor-changes"></a>
### Minor Changes
* api\_info\, api\_modify \- add <code>interface ethernet switch port\-isolation</code> which is supported since RouterOS 6\.43 \([https\://github\.com/ansible\-collections/community\.routeros/pull/375](https\://github\.com/ansible\-collections/community\.routeros/pull/375)\)\.
* api\_info\, api\_modify \- add <code>routing bfd configuration</code>\. Officially stabilized BFD support for BGP and OSPF is available since RouterOS 7\.11
\([https\://github\.com/ansible\-collections/community\.routeros/pull/375](https\://github\.com/ansible\-collections/community\.routeros/pull/375)\)\.
* api\_modify\, api\_info \- support API path <code>ip ipsec mode\-config</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/376](https\://github\.com/ansible\-collections/community\.routeros/pull/376)\)\.
<a id="v3-7-0"></a>
## v3\.7\.0
<a id="release-summary-2"></a>
### Release Summary
Feature release\.
<a id="minor-changes-1"></a>
### Minor Changes
* api\_find\_and\_modify \- allow to control whether <code>dynamic</code> and/or <code>builtin</code> entries are ignored with the new <code>ignore\_dynamic</code> and <code>ignore\_builtin</code> options \([https\://github\.com/ansible\-collections/community\.routeros/issues/372](https\://github\.com/ansible\-collections/community\.routeros/issues/372)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/373](https\://github\.com/ansible\-collections/community\.routeros/pull/373)\)\.
* api\_info\, api\_modify \- add <code>port\-cost\-mode</code> to <code>interface bridge</code> which is supported since RouterOS 7\.13 \([https\://github\.com/ansible\-collections/community\.routeros/pull/371](https\://github\.com/ansible\-collections/community\.routeros/pull/371)\)\.
<a id="v3-6-0"></a>
## v3\.6\.0
<a id="release-summary-3"></a>
### Release Summary
Feature release\.
<a id="minor-changes-2"></a>
### Minor Changes
* api\_info\, api\_modify \- add <code>mdns\-repeat\-ifaces</code> to <code>ip dns</code> for RouterOS 7\.16 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/358](https\://github\.com/ansible\-collections/community\.routeros/pull/358)\)\.
* api\_info\, api\_modify \- field name change in <code>routing bgp connection</code> path implemented by RouterOS 7\.19 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/360](https\://github\.com/ansible\-collections/community\.routeros/pull/360)\)\.
* api\_info\, api\_modify \- rename <code>is\-responder</code> property in <code>interface wireguard peers</code> to <code>responder</code> for RouterOS 7\.17 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/364](https\://github\.com/ansible\-collections/community\.routeros/pull/364)\)\.
<a id="v3-5-0"></a>
## v3\.5\.0
<a id="release-summary-4"></a>
### Release Summary
Feature release\.
<a id="minor-changes-3"></a>
### Minor Changes
* api\_info\, api\_modify \- change default for <code>/ip/cloud/ddns\-enabled</code> for RouterOS 7\.17 and newer from <code>yes</code> to <code>auto</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/350](https\://github\.com/ansible\-collections/community\.routeros/pull/350)\)\.
<a id="v3-4-0"></a>
## v3\.4\.0
<a id="release-summary-5"></a>
### Release Summary
Feature and bugfix release\.
<a id="minor-changes-4"></a>
### Minor Changes
* api\_info\, api\_modify \- add support for the <code>ip dns forwarders</code> path implemented by RouterOS 7\.17 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/343](https\://github\.com/ansible\-collections/community\.routeros/pull/343)\)\.
<a id="bugfixes-1"></a>
### Bugfixes
* api\_info\, api\_modify \- remove the primary key <code>action</code> from the <code>interface wifi provisioning</code> path\, since RouterOS also allows to create completely duplicate entries \([https\://github\.com/ansible\-collections/community\.routeros/issues/344](https\://github\.com/ansible\-collections/community\.routeros/issues/344)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/345](https\://github\.com/ansible\-collections/community\.routeros/pull/345)\)\.
<a id="v3-3-0"></a>
## v3\.3\.0
<a id="release-summary-6"></a>
### Release Summary
Feature release\.
<a id="minor-changes-5"></a>
### Minor Changes
* api\_info\, api\_modify \- add missing attribute <code>require\-message\-auth</code> for the <code>radius</code> path which exists since RouterOS version 7\.15 \([https\://github\.com/ansible\-collections/community\.routeros/issues/338](https\://github\.com/ansible\-collections/community\.routeros/issues/338)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/339](https\://github\.com/ansible\-collections/community\.routeros/pull/339)\)\.
* api\_info\, api\_modify \- add the <code>interface 6to4</code> path\. Used to manage IPv6 tunnels via tunnel\-brokers like HE\, where native IPv6 is not provided \([https\://github\.com/ansible\-collections/community\.routeros/pull/342](https\://github\.com/ansible\-collections/community\.routeros/pull/342)\)\.
* api\_info\, api\_modify \- add the <code>interface wireless access\-list</code> and <code>interface wireless connect\-list</code> paths \([https\://github\.com/ansible\-collections/community\.routeros/issues/284](https\://github\.com/ansible\-collections/community\.routeros/issues/284)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/340](https\://github\.com/ansible\-collections/community\.routeros/pull/340)\)\.
* api\_info\, api\_modify \- add the <code>use\-interface\-duid</code> option for <code>ipv6 dhcp\-client</code> path\. This option prevents issues with Fritzbox modems and routers\, when using virtual interfaces \(like VLANs\) may create duplicated records in hosts config\, this breaks original \"expose\-host\" function\. Also add the <code>script</code>\, <code>custom\-duid</code> and <code>validate\-server\-duid</code> as backport from 7\.15 version update \([https\://github\.com/ansible\-collections/community\.routeros/pull/341](https\://github\.com/ansible\-collections/community\.routeros/pull/341)\)\.
<a id="v3-2-0"></a>
## v3\.2\.0
<a id="release-summary-7"></a>
### Release Summary
Feature release\.
<a id="minor-changes-6"></a>
### Minor Changes
* api\_info\, api\_modify \- add support for the <code>routing filter community\-list</code> path implemented by RouterOS 7 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/331](https\://github\.com/ansible\-collections/community\.routeros/pull/331)\)\.
<a id="v3-1-0"></a>
## v3\.1\.0
<a id="release-summary-8"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-7"></a>
### Minor Changes
* api\_info\, api\_modify \- add missing fields <code>comment</code>\, <code>next\-pool</code> to <code>ip pool</code> path \([https\://github\.com/ansible\-collections/community\.routeros/pull/327](https\://github\.com/ansible\-collections/community\.routeros/pull/327)\)\.
<a id="bugfixes-2"></a>
### Bugfixes
* api\_info\, api\_modify \- fields <code>log</code> and <code>log\-prefix</code> in paths <code>ip firewall filter</code>\, <code>ip firewall mangle</code>\, <code>ip firewall nat</code>\, <code>ip firewall raw</code> now have the correct default values \([https\://github\.com/ansible\-collections/community\.routeros/pull/324](https\://github\.com/ansible\-collections/community\.routeros/pull/324)\)\.
<a id="v3-0-0"></a>
## v3\.0\.0
<a id="release-summary"></a>
<a id="release-summary-9"></a>
### Release Summary
Major release that drops support for End of Life Python versions and fixes check mode for community\.routeros\.command\.
@ -145,12 +310,12 @@ Major release that drops support for End of Life Python versions and fixes check
<a id="v2-20-0"></a>
## v2\.20\.0
<a id="release-summary-1"></a>
<a id="release-summary-10"></a>
### Release Summary
Feature release\.
<a id="minor-changes"></a>
<a id="minor-changes-8"></a>
### Minor Changes
* api\_info\, api\_modify \- add new parameters from the RouterOS 7\.16 release \([https\://github\.com/ansible\-collections/community\.routeros/pull/323](https\://github\.com/ansible\-collections/community\.routeros/pull/323)\)\.
@ -161,12 +326,12 @@ Feature release\.
<a id="v2-19-0"></a>
## v2\.19\.0
<a id="release-summary-2"></a>
<a id="release-summary-11"></a>
### Release Summary
Feature release\.
<a id="minor-changes-1"></a>
<a id="minor-changes-9"></a>
### Minor Changes
* api\_info\, api\_modify \- add support for the <code>ip dns adlist</code> path implemented by RouterOS 7\.15 and newer \([https\://github\.com/ansible\-collections/community\.routeros/pull/310](https\://github\.com/ansible\-collections/community\.routeros/pull/310)\)\.
@ -178,12 +343,12 @@ Feature release\.
<a id="v2-18-0"></a>
## v2\.18\.0
<a id="release-summary-3"></a>
<a id="release-summary-12"></a>
### Release Summary
Feature release\.
<a id="minor-changes-2"></a>
<a id="minor-changes-10"></a>
### Minor Changes
* api\_info \- allow to restrict the output by limiting fields to specific values with the new <code>restrict</code> option \([https\://github\.com/ansible\-collections/community\.routeros/pull/305](https\://github\.com/ansible\-collections/community\.routeros/pull/305)\)\.
@ -199,7 +364,7 @@ Feature release\.
* The collection deprecates support for all Ansible/ansible\-base/ansible\-core versions that are currently End of Life\, [according to the ansible\-core support matrix](https\://docs\.ansible\.com/ansible\-core/devel/reference\_appendices/release\_and\_maintenance\.html\#ansible\-core\-support\-matrix)\. This means that the next major release of the collection will no longer support Ansible 2\.9\, ansible\-base 2\.10\, ansible\-core 2\.11\, ansible\-core 2\.12\, ansible\-core 2\.13\, and ansible\-core 2\.14\.
<a id="bugfixes"></a>
<a id="bugfixes-3"></a>
### Bugfixes
* api\_modify\, api\_info \- change the default of <code>ingress\-filtering</code> in paths <code>interface bridge</code> and <code>interface bridge port</code> back to <code>false</code> for RouterOS before version 7 \([https\://github\.com/ansible\-collections/community\.routeros/pull/305](https\://github\.com/ansible\-collections/community\.routeros/pull/305)\)\.
@ -207,12 +372,12 @@ Feature release\.
<a id="v2-17-0"></a>
## v2\.17\.0
<a id="release-summary-4"></a>
<a id="release-summary-13"></a>
### Release Summary
Feature release\.
<a id="minor-changes-3"></a>
<a id="minor-changes-11"></a>
### Minor Changes
* api\_info\, api\_modify \- add <code>system health settings</code> path \([https\://github\.com/ansible\-collections/community\.routeros/pull/294](https\://github\.com/ansible\-collections/community\.routeros/pull/294)\)\.
@ -222,12 +387,12 @@ Feature release\.
<a id="v2-16-0"></a>
## v2\.16\.0
<a id="release-summary-5"></a>
<a id="release-summary-14"></a>
### Release Summary
Feature release\.
<a id="minor-changes-4"></a>
<a id="minor-changes-12"></a>
### Minor Changes
* api\_info\, api\_modify \- add missing path <code>/ppp secret</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/286](https\://github\.com/ansible\-collections/community\.routeros/pull/286)\)\.
@ -236,12 +401,12 @@ Feature release\.
<a id="v2-15-0"></a>
## v2\.15\.0
<a id="release-summary-6"></a>
<a id="release-summary-15"></a>
### Release Summary
Feature release\.
<a id="minor-changes-5"></a>
<a id="minor-changes-13"></a>
### Minor Changes
* api\_info\, api\_modify \- Add RouterOS 7\.x support to <code>/mpls ldp</code> path \([https\://github\.com/ansible\-collections/community\.routeros/pull/271](https\://github\.com/ansible\-collections/community\.routeros/pull/271)\)\.
@ -258,12 +423,12 @@ Feature release\.
<a id="v2-14-0"></a>
## v2\.14\.0
<a id="release-summary-7"></a>
<a id="release-summary-16"></a>
### Release Summary
Feature release\.
<a id="minor-changes-6"></a>
<a id="minor-changes-14"></a>
### Minor Changes
* api\_info\, api\_modify \- add read\-only fields <code>installed\-version</code>\, <code>latest\-version</code> and <code>status</code> in <code>system package update</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/263](https\://github\.com/ansible\-collections/community\.routeros/pull/263)\)\.
@ -273,18 +438,18 @@ Feature release\.
<a id="v2-13-0"></a>
## v2\.13\.0
<a id="release-summary-8"></a>
<a id="release-summary-17"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-7"></a>
<a id="minor-changes-15"></a>
### Minor Changes
* api\_info\, api\_modify \- make path <code>user group</code> modifiable and add <code>comment</code> attribute \([https\://github\.com/ansible\-collections/community\.routeros/issues/256](https\://github\.com/ansible\-collections/community\.routeros/issues/256)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/257](https\://github\.com/ansible\-collections/community\.routeros/pull/257)\)\.
* api\_modify\, api\_info \- add support for the <code>ip vrf</code> path in RouterOS 7 \([https\://github\.com/ansible\-collections/community\.routeros/pull/259](https\://github\.com/ansible\-collections/community\.routeros/pull/259)\)
<a id="bugfixes-1"></a>
<a id="bugfixes-4"></a>
### Bugfixes
* facts \- fix date not getting removed for idempotent config export \([https\://github\.com/ansible\-collections/community\.routeros/pull/262](https\://github\.com/ansible\-collections/community\.routeros/pull/262)\)\.
@ -292,12 +457,12 @@ Bugfix and feature release\.
<a id="v2-12-0"></a>
## v2\.12\.0
<a id="release-summary-9"></a>
<a id="release-summary-18"></a>
### Release Summary
Feature release\.
<a id="minor-changes-8"></a>
<a id="minor-changes-16"></a>
### Minor Changes
* api\_info\, api\_modify \- add <code>interface ovpn\-client</code> path \([https\://github\.com/ansible\-collections/community\.routeros/issues/242](https\://github\.com/ansible\-collections/community\.routeros/issues/242)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/244](https\://github\.com/ansible\-collections/community\.routeros/pull/244)\)\.
@ -311,12 +476,12 @@ Feature release\.
<a id="v2-11-0"></a>
## v2\.11\.0
<a id="release-summary-10"></a>
<a id="release-summary-19"></a>
### Release Summary
Feature and bugfix release\.
<a id="minor-changes-9"></a>
<a id="minor-changes-17"></a>
### Minor Changes
* api\_info\, api\_modify \- add missing DoH parameters <code>doh\-max\-concurrent\-queries</code>\, <code>doh\-max\-server\-connections</code>\, and <code>doh\-timeout</code> to the <code>ip dns</code> path \([https\://github\.com/ansible\-collections/community\.routeros/issues/230](https\://github\.com/ansible\-collections/community\.routeros/issues/230)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/235](https\://github\.com/ansible\-collections/community\.routeros/pull/235)\)
@ -331,12 +496,12 @@ Feature and bugfix release\.
<a id="v2-10-0"></a>
## v2\.10\.0
<a id="release-summary-11"></a>
<a id="release-summary-20"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-10"></a>
<a id="minor-changes-18"></a>
### Minor Changes
* api\_info \- add new <code>include\_read\_only</code> option to select behavior for read\-only values\. By default these are not returned \([https\://github\.com/ansible\-collections/community\.routeros/pull/213](https\://github\.com/ansible\-collections/community\.routeros/pull/213)\)\.
@ -360,7 +525,7 @@ Bugfix and feature release\.
* api\_modify \- add new <code>handle\_read\_only</code> and <code>handle\_write\_only</code> options to handle the module\'s behavior for read\-only and write\-only fields \([https\://github\.com/ansible\-collections/community\.routeros/pull/213](https\://github\.com/ansible\-collections/community\.routeros/pull/213)\)\.
* api\_modify\, api\_info \- support API paths <code>routing id</code>\, <code>routing bgp connection</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/220](https\://github\.com/ansible\-collections/community\.routeros/pull/220)\)\.
<a id="bugfixes-2"></a>
<a id="bugfixes-5"></a>
### Bugfixes
* api\_info\, api\_modify \- in the <code>snmp</code> path\, ensure that <code>engine\-id\-suffix</code> is only available on RouterOS 7\.10\+\, and that <code>engine\-id</code> is read\-only on RouterOS 7\.10\+ \([https\://github\.com/ansible\-collections/community\.routeros/issues/208](https\://github\.com/ansible\-collections/community\.routeros/issues/208)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/218](https\://github\.com/ansible\-collections/community\.routeros/pull/218)\)\.
@ -368,18 +533,18 @@ Bugfix and feature release\.
<a id="v2-9-0"></a>
## v2\.9\.0
<a id="release-summary-12"></a>
<a id="release-summary-21"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-11"></a>
<a id="minor-changes-19"></a>
### Minor Changes
* api\_info\, api\_modify \- add path <code>caps\-man channel</code> and enable path <code>caps\-man manager interface</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/193](https\://github\.com/ansible\-collections/community\.routeros/issues/193)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/194](https\://github\.com/ansible\-collections/community\.routeros/pull/194)\)\.
* api\_info\, api\_modify \- add path <code>ip traffic\-flow target</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/191](https\://github\.com/ansible\-collections/community\.routeros/issues/191)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/192](https\://github\.com/ansible\-collections/community\.routeros/pull/192)\)\.
<a id="bugfixes-3"></a>
<a id="bugfixes-6"></a>
### Bugfixes
* api\_modify\, api\_info \- add missing parameter <code>engine\-id\-suffix</code> for the <code>snmp</code> path \([https\://github\.com/ansible\-collections/community\.routeros/issues/189](https\://github\.com/ansible\-collections/community\.routeros/issues/189)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/190](https\://github\.com/ansible\-collections/community\.routeros/pull/190)\)\.
@ -387,7 +552,7 @@ Bugfix and feature release\.
<a id="v2-8-3"></a>
## v2\.8\.3
<a id="release-summary-13"></a>
<a id="release-summary-22"></a>
### Release Summary
Maintenance release with updated documentation\.
@ -408,12 +573,12 @@ for the rendered HTML version of the documentation of the latest release\.
<a id="v2-8-2"></a>
## v2\.8\.2
<a id="release-summary-14"></a>
<a id="release-summary-23"></a>
### Release Summary
Bugfix release\.
<a id="bugfixes-4"></a>
<a id="bugfixes-7"></a>
### Bugfixes
* api\_modify\, api\_info \- add missing parameter <code>tls</code> for the <code>tool e\-mail</code> path \([https\://github\.com/ansible\-collections/community\.routeros/issues/179](https\://github\.com/ansible\-collections/community\.routeros/issues/179)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/180](https\://github\.com/ansible\-collections/community\.routeros/pull/180)\)\.
@ -421,12 +586,12 @@ Bugfix release\.
<a id="v2-8-1"></a>
## v2\.8\.1
<a id="release-summary-15"></a>
<a id="release-summary-24"></a>
### Release Summary
Bugfix release\.
<a id="bugfixes-5"></a>
<a id="bugfixes-8"></a>
### Bugfixes
* facts \- do not crash in CLI output preprocessing in unexpected situations during line unwrapping \([https\://github\.com/ansible\-collections/community\.routeros/issues/170](https\://github\.com/ansible\-collections/community\.routeros/issues/170)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/177](https\://github\.com/ansible\-collections/community\.routeros/pull/177)\)\.
@ -434,12 +599,12 @@ Bugfix release\.
<a id="v2-8-0"></a>
## v2\.8\.0
<a id="release-summary-16"></a>
<a id="release-summary-25"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-12"></a>
<a id="minor-changes-20"></a>
### Minor Changes
* api\_modify \- adapt data for API paths <code>ip dhcp\-server network</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/156](https\://github\.com/ansible\-collections/community\.routeros/pull/156)\)\.
@ -449,7 +614,7 @@ Bugfix and feature release\.
* api\_modify \- support API paths <code>ip firewall layer7\-protocol</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/153](https\://github\.com/ansible\-collections/community\.routeros/pull/153)\)\.
* command \- workaround for extra characters in stdout in RouterOS versions between 6\.49 and 7\.1\.5 \([https\://github\.com/ansible\-collections/community\.routeros/issues/62](https\://github\.com/ansible\-collections/community\.routeros/issues/62)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/161](https\://github\.com/ansible\-collections/community\.routeros/pull/161)\)\.
<a id="bugfixes-6"></a>
<a id="bugfixes-9"></a>
### Bugfixes
* api\_info\, api\_modify \- fix default and remove behavior for <code>dhcp\-options</code> in path <code>ip dhcp\-client</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/148](https\://github\.com/ansible\-collections/community\.routeros/issues/148)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/154](https\://github\.com/ansible\-collections/community\.routeros/pull/154)\)\.
@ -459,17 +624,17 @@ Bugfix and feature release\.
<a id="v2-7-0"></a>
## v2\.7\.0
<a id="release-summary-17"></a>
<a id="release-summary-26"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-13"></a>
<a id="minor-changes-21"></a>
### Minor Changes
* api\_modify\, api\_info \- support API paths <code>ip arp</code>\, <code>ip firewall raw</code>\, <code>ipv6 firewall raw</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/144](https\://github\.com/ansible\-collections/community\.routeros/pull/144)\)\.
<a id="bugfixes-7"></a>
<a id="bugfixes-10"></a>
### Bugfixes
* api\_modify\, api\_info \- defaults corrected for fields in <code>interface wireguard peers</code> API path \([https\://github\.com/ansible\-collections/community\.routeros/pull/144](https\://github\.com/ansible\-collections/community\.routeros/pull/144)\)\.
@ -477,18 +642,18 @@ Bugfix and feature release\.
<a id="v2-6-0"></a>
## v2\.6\.0
<a id="release-summary-18"></a>
<a id="release-summary-27"></a>
### Release Summary
Regular bugfix and feature release\.
<a id="minor-changes-14"></a>
<a id="minor-changes-22"></a>
### Minor Changes
* api\_modify\, api\_info \- add field <code>regexp</code> to <code>ip dns static</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/141](https\://github\.com/ansible\-collections/community\.routeros/issues/141)\)\.
* api\_modify\, api\_info \- support API paths <code>interface wireguard</code>\, <code>interface wireguard peers</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/143](https\://github\.com/ansible\-collections/community\.routeros/pull/143)\)\.
<a id="bugfixes-8"></a>
<a id="bugfixes-11"></a>
### Bugfixes
* api\_modify \- do not use <code>name</code> as a unique key in <code>ip dns static</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/141](https\://github\.com/ansible\-collections/community\.routeros/issues/141)\)\.
@ -497,17 +662,17 @@ Regular bugfix and feature release\.
<a id="v2-5-0"></a>
## v2\.5\.0
<a id="release-summary-19"></a>
<a id="release-summary-28"></a>
### Release Summary
Feature and bugfix release\.
<a id="minor-changes-15"></a>
<a id="minor-changes-23"></a>
### Minor Changes
* api\_info\, api\_modify \- support API paths <code>interface ethernet poe</code>\, <code>interface gre6</code>\, <code>interface vrrp</code> and also support all previously missing fields of entries in <code>ip dhcp\-server</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/137](https\://github\.com/ansible\-collections/community\.routeros/pull/137)\)\.
<a id="bugfixes-9"></a>
<a id="bugfixes-12"></a>
### Bugfixes
* api\_modify \- <code>address\-pool</code> field of entries in API path <code>ip dhcp\-server</code> is not required anymore \([https\://github\.com/ansible\-collections/community\.routeros/pull/137](https\://github\.com/ansible\-collections/community\.routeros/pull/137)\)\.
@ -515,12 +680,12 @@ Feature and bugfix release\.
<a id="v2-4-0"></a>
## v2\.4\.0
<a id="release-summary-20"></a>
<a id="release-summary-29"></a>
### Release Summary
Feature release improving the <code>api\*</code> modules\.
<a id="minor-changes-16"></a>
<a id="minor-changes-24"></a>
### Minor Changes
* api\* modules \- Add new option <code>force\_no\_cert</code> to connect with ADH ciphers \([https\://github\.com/ansible\-collections/community\.routeros/pull/124](https\://github\.com/ansible\-collections/community\.routeros/pull/124)\)\.
@ -541,7 +706,7 @@ Feature release improving the <code>api\*</code> modules\.
* api\_modify\, api\_info \- support for fields <code>blackhole</code>\, <code>pref\-src</code>\, <code>routing\-table</code>\, <code>suppress\-hw\-offload</code>\, <code>type</code>\, <code>vrf\-interface</code> in <code>ip route</code> path \([https\://github\.com/ansible\-collections/community\.routeros/pull/131](https\://github\.com/ansible\-collections/community\.routeros/pull/131)\)\.
* api\_modify\, api\_info \- support paths <code>system ntp client servers</code> and <code>system ntp server</code> available in ROS7\, as well as new fields <code>servers</code>\, <code>mode</code>\, and <code>vrf</code> for <code>system ntp client</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/122](https\://github\.com/ansible\-collections/community\.routeros/pull/122)\)\.
<a id="bugfixes-10"></a>
<a id="bugfixes-13"></a>
### Bugfixes
* api\_modify \- <code>ip route</code> entry can be defined without the need of <code>gateway</code> field\, which is correct for unreachable/blackhole type of routes \([https\://github\.com/ansible\-collections/community\.routeros/pull/131](https\://github\.com/ansible\-collections/community\.routeros/pull/131)\)\.
@ -559,7 +724,7 @@ Feature release improving the <code>api\*</code> modules\.
<a id="v2-3-1"></a>
## v2\.3\.1
<a id="release-summary-21"></a>
<a id="release-summary-30"></a>
### Release Summary
Maintenance release with improved documentation\.
@ -572,19 +737,19 @@ Maintenance release with improved documentation\.
<a id="v2-3-0"></a>
## v2\.3\.0
<a id="release-summary-22"></a>
<a id="release-summary-31"></a>
### Release Summary
Feature and bugfix release\.
<a id="minor-changes-17"></a>
<a id="minor-changes-25"></a>
### Minor Changes
* The collection repository conforms to the [REUSE specification](https\://reuse\.software/spec/) except for the changelog fragments \([https\://github\.com/ansible\-collections/community\.routeros/pull/108](https\://github\.com/ansible\-collections/community\.routeros/pull/108)\)\.
* api\* modules \- added <code>timeout</code> parameter \([https\://github\.com/ansible\-collections/community\.routeros/pull/109](https\://github\.com/ansible\-collections/community\.routeros/pull/109)\)\.
* api\_modify\, api\_info \- support API path <code>ip firewall mangle</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/110](https\://github\.com/ansible\-collections/community\.routeros/pull/110)\)\.
<a id="bugfixes-11"></a>
<a id="bugfixes-14"></a>
### Bugfixes
* api\_modify\, api\_info \- make API path <code>ip dhcp\-server</code> support <code>script</code>\, and <code>ip firewall nat</code> support <code>in\-interface</code> and <code>in\-interface\-list</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/110](https\://github\.com/ansible\-collections/community\.routeros/pull/110)\)\.
@ -592,12 +757,12 @@ Feature and bugfix release\.
<a id="v2-2-1"></a>
## v2\.2\.1
<a id="release-summary-23"></a>
<a id="release-summary-32"></a>
### Release Summary
Bugfix release\.
<a id="bugfixes-12"></a>
<a id="bugfixes-15"></a>
### Bugfixes
* api\_modify\, api\_info \- make API path <code>ip dhcp\-server lease</code> support <code>server\=all</code> \([https\://github\.com/ansible\-collections/community\.routeros/issues/104](https\://github\.com/ansible\-collections/community\.routeros/issues/104)\, [https\://github\.com/ansible\-collections/community\.routeros/pull/107](https\://github\.com/ansible\-collections/community\.routeros/pull/107)\)\.
@ -606,17 +771,17 @@ Bugfix release\.
<a id="v2-2-0"></a>
## v2\.2\.0
<a id="release-summary-24"></a>
<a id="release-summary-33"></a>
### Release Summary
New feature release\.
<a id="minor-changes-18"></a>
<a id="minor-changes-26"></a>
### Minor Changes
* All software licenses are now in the <code>LICENSES/</code> directory of the collection root\. Moreover\, <code>SPDX\-License\-Identifier\:</code> is used to declare the applicable license for every file that is not automatically generated \([https\://github\.com/ansible\-collections/community\.routeros/pull/101](https\://github\.com/ansible\-collections/community\.routeros/pull/101)\)\.
<a id="bugfixes-13"></a>
<a id="bugfixes-16"></a>
### Bugfixes
* Include <code>LICENSES/BSD\-2\-Clause\.txt</code> file for the <code>routeros</code> module utils \([https\://github\.com/ansible\-collections/community\.routeros/pull/101](https\://github\.com/ansible\-collections/community\.routeros/pull/101)\)\.
@ -630,12 +795,12 @@ New feature release\.
<a id="v2-1-0"></a>
## v2\.1\.0
<a id="release-summary-25"></a>
<a id="release-summary-34"></a>
### Release Summary
Feature and bugfix release with new modules\.
<a id="minor-changes-19"></a>
<a id="minor-changes-27"></a>
### Minor Changes
* Added a <code>community\.routeros\.api</code> module defaults group\. Use with <code>group/community\.routeros\.api</code> to provide options for all API\-based modules \([https\://github\.com/ansible\-collections/community\.routeros/pull/89](https\://github\.com/ansible\-collections/community\.routeros/pull/89)\)\.
@ -644,7 +809,7 @@ Feature and bugfix release with new modules\.
* api \- update <code>query</code> to accept symbolic parameters \([https\://github\.com/ansible\-collections/community\.routeros/pull/63](https\://github\.com/ansible\-collections/community\.routeros/pull/63)\)\.
* api\* modules \- allow to set an encoding other than the default ASCII for communicating with the API \([https\://github\.com/ansible\-collections/community\.routeros/pull/95](https\://github\.com/ansible\-collections/community\.routeros/pull/95)\)\.
<a id="bugfixes-14"></a>
<a id="bugfixes-17"></a>
### Bugfixes
* query \- fix query function check for <code>\.id</code> vs\. <code>id</code> arguments to not conflict with routeros arguments like <code>identity</code> \([https\://github\.com/ansible\-collections/community\.routeros/pull/68](https\://github\.com/ansible\-collections/community\.routeros/pull/68)\, [https\://github\.com/ansible\-collections/community\.routeros/issues/67](https\://github\.com/ansible\-collections/community\.routeros/issues/67)\)\.
@ -659,12 +824,12 @@ Feature and bugfix release with new modules\.
<a id="v2-0-0"></a>
## v2\.0\.0
<a id="release-summary-26"></a>
<a id="release-summary-35"></a>
### Release Summary
A new major release with breaking changes in the behavior of <code>community\.routeros\.api</code> and <code>community\.routeros\.command</code>\.
<a id="minor-changes-20"></a>
<a id="minor-changes-28"></a>
### Minor Changes
* api \- make validation of <code>WHERE</code> for <code>query</code> more strict \([https\://github\.com/ansible\-collections/community\.routeros/pull/53](https\://github\.com/ansible\-collections/community\.routeros/pull/53)\)\.
@ -678,7 +843,7 @@ A new major release with breaking changes in the behavior of <code>community\.ro
* api \- splitting commands no longer uses a naive split by whitespace\, but a more RouterOS CLI compatible splitting algorithm \([https\://github\.com/ansible\-collections/community\.routeros/pull/45](https\://github\.com/ansible\-collections/community\.routeros/pull/45)\)\.
* command \- the module now always indicates that a change happens\. If this is not correct\, please use <code>changed\_when</code> to determine the correct changed status for a task \([https\://github\.com/ansible\-collections/community\.routeros/pull/50](https\://github\.com/ansible\-collections/community\.routeros/pull/50)\)\.
<a id="bugfixes-15"></a>
<a id="bugfixes-18"></a>
### Bugfixes
* api \- improve splitting of <code>WHERE</code> queries \([https\://github\.com/ansible\-collections/community\.routeros/pull/47](https\://github\.com/ansible\-collections/community\.routeros/pull/47)\)\.
@ -700,12 +865,12 @@ A new major release with breaking changes in the behavior of <code>community\.ro
<a id="v1-2-0"></a>
## v1\.2\.0
<a id="release-summary-27"></a>
<a id="release-summary-36"></a>
### Release Summary
Bugfix and feature release\.
<a id="minor-changes-21"></a>
<a id="minor-changes-29"></a>
### Minor Changes
* Avoid internal ansible\-core module\_utils in favor of equivalent public API available since at least Ansible 2\.9 \([https\://github\.com/ansible\-collections/community\.routeros/pull/38](https\://github\.com/ansible\-collections/community\.routeros/pull/38)\)\.
@ -713,7 +878,7 @@ Bugfix and feature release\.
* api \- rename option <code>ssl</code> to <code>tls</code>\, and keep the old name as an alias \([https\://github\.com/ansible\-collections/community\.routeros/pull/37](https\://github\.com/ansible\-collections/community\.routeros/pull/37)\)\.
* fact \- add fact <code>ansible\_net\_config\_nonverbose</code> to get idempotent config \(no date\, no verbose\) \([https\://github\.com/ansible\-collections/community\.routeros/pull/23](https\://github\.com/ansible\-collections/community\.routeros/pull/23)\)\.
<a id="bugfixes-16"></a>
<a id="bugfixes-19"></a>
### Bugfixes
* api \- when using TLS/SSL\, remove explicit cipher configuration to insecure values\, which also makes it impossible to connect to newer RouterOS versions \([https\://github\.com/ansible\-collections/community\.routeros/pull/34](https\://github\.com/ansible\-collections/community\.routeros/pull/34)\)\.
@ -721,12 +886,12 @@ Bugfix and feature release\.
<a id="v1-1-0"></a>
## v1\.1\.0
<a id="release-summary-28"></a>
<a id="release-summary-37"></a>
### Release Summary
This release allow dashes in usernames for SSH\-based modules\.
<a id="minor-changes-22"></a>
<a id="minor-changes-30"></a>
### Minor Changes
* command \- added support for a dash \(<code>\-</code>\) in username \([https\://github\.com/ansible\-collections/community\.routeros/pull/18](https\://github\.com/ansible\-collections/community\.routeros/pull/18)\)\.
@ -735,12 +900,12 @@ This release allow dashes in usernames for SSH\-based modules\.
<a id="v1-0-1"></a>
## v1\.0\.1
<a id="release-summary-29"></a>
<a id="release-summary-38"></a>
### Release Summary
Maintenance release with a bugfix for <code>api</code>\.
<a id="bugfixes-17"></a>
<a id="bugfixes-20"></a>
### Bugfixes
* api \- remove <code>id to \.id</code> as default requirement which conflicts with RouterOS <code>id</code> configuration parameter \([https\://github\.com/ansible\-collections/community\.routeros/pull/15](https\://github\.com/ansible\-collections/community\.routeros/pull/15)\)\.
@ -748,12 +913,12 @@ Maintenance release with a bugfix for <code>api</code>\.
<a id="v1-0-0"></a>
## v1\.0\.0
<a id="release-summary-30"></a>
<a id="release-summary-39"></a>
### Release Summary
This is the first production \(non\-prerelease\) release of <code>community\.routeros</code>\.
<a id="bugfixes-18"></a>
<a id="bugfixes-21"></a>
### Bugfixes
* routeros terminal plugin \- allow slashes in hostnames for terminal detection\. Without this\, slashes in hostnames will result in connection timeouts \([https\://github\.com/ansible\-collections/community\.network/pull/138](https\://github\.com/ansible\-collections/community\.network/pull/138)\)\.
@ -761,12 +926,12 @@ This is the first production \(non\-prerelease\) release of <code>community\.rou
<a id="v0-1-1"></a>
## v0\.1\.1
<a id="release-summary-31"></a>
<a id="release-summary-40"></a>
### Release Summary
Small improvements and bugfixes over the initial release\.
<a id="bugfixes-19"></a>
<a id="bugfixes-22"></a>
### Bugfixes
* api \- fix crash when the <code>ssl</code> parameter is used \([https\://github\.com/ansible\-collections/community\.routeros/pull/3](https\://github\.com/ansible\-collections/community\.routeros/pull/3)\)\.
@ -774,12 +939,12 @@ Small improvements and bugfixes over the initial release\.
<a id="v0-1-0"></a>
## v0\.1\.0
<a id="release-summary-32"></a>
<a id="release-summary-41"></a>
### Release Summary
The <code>community\.routeros</code> continues the work on the Ansible RouterOS modules from their state in <code>community\.network</code> 1\.2\.0\. The changes listed here are thus relative to the modules <code>community\.network\.routeros\_\*</code>\.
<a id="minor-changes-23"></a>
<a id="minor-changes-31"></a>
### Minor Changes
* facts \- now also collecting data about BGP and OSPF \([https\://github\.com/ansible\-collections/community\.network/pull/101](https\://github\.com/ansible\-collections/community\.network/pull/101)\)\.

View file

@ -4,6 +4,142 @@ Community RouterOS Release Notes
.. contents:: Topics
v3.8.1
======
Release Summary
---------------
Bugfix release.
Bugfixes
--------
- facts and api_facts modules - prevent deprecation warnings when used with ansible-core 2.19 (https://github.com/ansible-collections/community.routeros/pull/384).
v3.8.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_info, api_modify - add ``interface ethernet switch port-isolation`` which is supported since RouterOS 6.43 (https://github.com/ansible-collections/community.routeros/pull/375).
- api_info, api_modify - add ``routing bfd configuration``. Officially stabilized BFD support for BGP and OSPF is available since RouterOS 7.11
(https://github.com/ansible-collections/community.routeros/pull/375).
- api_modify, api_info - support API path ``ip ipsec mode-config`` (https://github.com/ansible-collections/community.routeros/pull/376).
v3.7.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_find_and_modify - allow to control whether ``dynamic`` and/or ``builtin`` entries are ignored with the new ``ignore_dynamic`` and ``ignore_builtin`` options (https://github.com/ansible-collections/community.routeros/issues/372, https://github.com/ansible-collections/community.routeros/pull/373).
- api_info, api_modify - add ``port-cost-mode`` to ``interface bridge`` which is supported since RouterOS 7.13 (https://github.com/ansible-collections/community.routeros/pull/371).
v3.6.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_info, api_modify - add ``mdns-repeat-ifaces`` to ``ip dns`` for RouterOS 7.16 and newer (https://github.com/ansible-collections/community.routeros/pull/358).
- api_info, api_modify - field name change in ``routing bgp connection`` path implemented by RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/360).
- api_info, api_modify - rename ``is-responder`` property in ``interface wireguard peers`` to ``responder`` for RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/364).
v3.5.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_info, api_modify - change default for ``/ip/cloud/ddns-enabled`` for RouterOS 7.17 and newer from ``yes`` to ``auto`` (https://github.com/ansible-collections/community.routeros/pull/350).
v3.4.0
======
Release Summary
---------------
Feature and bugfix release.
Minor Changes
-------------
- api_info, api_modify - add support for the ``ip dns forwarders`` path implemented by RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/343).
Bugfixes
--------
- api_info, api_modify - remove the primary key ``action`` from the ``interface wifi provisioning`` path, since RouterOS also allows to create completely duplicate entries (https://github.com/ansible-collections/community.routeros/issues/344, https://github.com/ansible-collections/community.routeros/pull/345).
v3.3.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_info, api_modify - add missing attribute ``require-message-auth`` for the ``radius`` path which exists since RouterOS version 7.15 (https://github.com/ansible-collections/community.routeros/issues/338, https://github.com/ansible-collections/community.routeros/pull/339).
- api_info, api_modify - add the ``interface 6to4`` path. Used to manage IPv6 tunnels via tunnel-brokers like HE, where native IPv6 is not provided (https://github.com/ansible-collections/community.routeros/pull/342).
- api_info, api_modify - add the ``interface wireless access-list`` and ``interface wireless connect-list`` paths (https://github.com/ansible-collections/community.routeros/issues/284, https://github.com/ansible-collections/community.routeros/pull/340).
- api_info, api_modify - add the ``use-interface-duid`` option for ``ipv6 dhcp-client`` path. This option prevents issues with Fritzbox modems and routers, when using virtual interfaces (like VLANs) may create duplicated records in hosts config, this breaks original "expose-host" function. Also add the ``script``, ``custom-duid`` and ``validate-server-duid`` as backport from 7.15 version update (https://github.com/ansible-collections/community.routeros/pull/341).
v3.2.0
======
Release Summary
---------------
Feature release.
Minor Changes
-------------
- api_info, api_modify - add support for the ``routing filter community-list`` path implemented by RouterOS 7 and newer (https://github.com/ansible-collections/community.routeros/pull/331).
v3.1.0
======
Release Summary
---------------
Bugfix and feature release.
Minor Changes
-------------
- api_info, api_modify - add missing fields ``comment``, ``next-pool`` to ``ip pool`` path (https://github.com/ansible-collections/community.routeros/pull/327).
Bugfixes
--------
- api_info, api_modify - fields ``log`` and ``log-prefix`` in paths ``ip firewall filter``, ``ip firewall mangle``, ``ip firewall nat``, ``ip firewall raw`` now have the correct default values (https://github.com/ansible-collections/community.routeros/pull/324).
v3.0.0
======

View file

@ -5,7 +5,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
-->
# Community RouterOS Collection
[![CI](https://github.com/ansible-collections/community.routeros/workflows/CI/badge.svg?event=push)](https://github.com/ansible-collections/community.routeros/actions)
[![Documentation](https://img.shields.io/badge/docs-brightgreen.svg)](https://docs.ansible.com/ansible/devel/collections/community/routeros/)
[![CI](https://github.com/ansible-collections/community.routeros/actions/workflows/nox.yml/badge.svg?branch=main)](https://github.com/ansible-collections/community.routeros/actions)
[![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.routeros)](https://codecov.io/gh/ansible-collections/community.routeros)
[![REUSE status](https://api.reuse.software/badge/github.com/ansible-collections/community.routeros)](https://api.reuse.software/info/github.com/ansible-collections/community.routeros)
@ -33,11 +34,11 @@ For more information about communication, see the [Ansible communication guide](
## Tested with Ansible
Tested with the current ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, and ansible-core 2.18 releases and the current development version of ansible-core. Ansible 2.9, ansible-base 2.10, and ansible-core versions before 2.15.0 are not supported.
Tested with the current ansible-core 2.15, ansible-core 2.16, ansible-core 2.17, ansible-core 2.18, and ansible-core 2.19 releases and the current development version of ansible-core. Ansible 2.9, ansible-base 2.10, and ansible-core versions before 2.15.0 are not supported.
## External requirements
The exact requirements for every module are listed in the module documentation.
The exact requirements for every module are listed in the module documentation.
### Supported connections
@ -207,4 +208,4 @@ See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/commu
Parts of the collection are licensed under the [BSD 2-Clause license](https://github.com/ansible-collections/community.routeros/blob/main/LICENSES/BSD-2-Clause.txt).
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `.reuse/dep5`. This conforms to the [REUSE specification](https://reuse.software/spec/).
All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/).

11
REUSE.toml Normal file
View file

@ -0,0 +1,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
version = 1
[[annotations]]
path = "changelogs/fragments/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "Ansible Project"
SPDX-License-Identifier = "GPL-3.0-or-later"

97
antsibull-nox.toml Normal file
View file

@ -0,0 +1,97 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
[collection_sources]
"community.internal_test_tools" = "git+https://github.com/ansible-collections/community.internal_test_tools.git,main"
"community.netcommon" = "git+https://github.com/ansible-collections/ansible.netcommon.git,main"
"community.utils" = "git+https://github.com/ansible-collections/ansible.utils.git,main"
[sessions]
[sessions.lint]
run_isort = false
run_black = false
run_flake8 = false
run_pylint = false
run_yamllint = true
yamllint_config = ".yamllint"
yamllint_config_plugins = ".yamllint-docs"
yamllint_config_plugins_examples = ".yamllint-examples"
yamllint_config_extra_docs = ".yamllint-extra-docs"
run_mypy = false
[sessions.docs_check]
validate_collection_refs="all"
codeblocks_restrict_types = [
"ansible-output",
"ini",
"yaml",
"yaml+jinja",
]
codeblocks_restrict_type_exact_case = true
codeblocks_allow_without_type = false
codeblocks_allow_literal_blocks = false
[sessions.license_check]
[sessions.extra_checks]
run_no_unwanted_files = true
no_unwanted_files_module_extensions = [".py"]
no_unwanted_files_yaml_extensions = [".yml"]
run_action_groups = true
run_no_trailing_whitespace = true
no_trailing_whitespace_skip_directories = [
"tests/unit/plugins/modules/fixtures/",
]
run_avoid_characters = true
[[sessions.extra_checks.action_groups_config]]
name = "api"
pattern = "^api.*$"
exclusions = []
doc_fragment = "community.routeros.attributes.actiongroup_api"
[[sessions.extra_checks.avoid_character_group]]
name = "tab"
regex = "\\x09"
[sessions.build_import_check]
run_galaxy_importer = true
[sessions.ansible_test_sanity]
include_devel = true
[sessions.ansible_test_units]
include_devel = true
[sessions.ansible_test_integration_w_default_container]
include_devel = true
controller_python_versions_only = true
[sessions.ansible_test_integration_w_default_container.core_python_versions]
"2.15" = ["2.7", "3.6", "3.7"]
"2.16" = ["3.10"]
"2.17" = ["3.8"]
"2.18" = ["3.9"]
"2.19" = ["3.11"]
[[sessions.ee_check.execution_environments]]
name = "devel-ubi-9"
description = "ansible-core devel @ RHEL UBI 9"
test_playbooks = ["tests/ee/all.yml"]
config.images.base_image.name = "docker.io/redhat/ubi9:latest"
config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/devel.tar.gz"
config.dependencies.ansible_runner.package_pip = "ansible-runner"
config.dependencies.python_interpreter.package_system = "python3.12 python3.12-pip python3.12-wheel python3.12-cryptography"
config.dependencies.python_interpreter.python_path = "/usr/bin/python3.12"
runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"}
[[sessions.ee_check.execution_environments]]
name = "2.15-rocky-9"
description = "ansible-core 2.15 @ Rocky Linux 9"
test_playbooks = ["tests/ee/all.yml"]
config.images.base_image.name = "quay.io/rockylinux/rockylinux:9"
config.dependencies.ansible_core.package_pip = "https://github.com/ansible/ansible/archive/stable-2.15.tar.gz"
config.dependencies.ansible_runner.package_pip = "ansible-runner"
runtime_environment = {"ANSIBLE_PRIVATE_ROLE_VARS" = "true"}

View file

@ -809,3 +809,138 @@ releases:
fragments:
- 3.0.0.yml
release_date: '2024-10-20'
3.1.0:
changes:
bugfixes:
- api_info, api_modify - fields ``log`` and ``log-prefix`` in paths ``ip firewall
filter``, ``ip firewall mangle``, ``ip firewall nat``, ``ip firewall raw``
now have the correct default values (https://github.com/ansible-collections/community.routeros/pull/324).
minor_changes:
- api_info, api_modify - add missing fields ``comment``, ``next-pool`` to
``ip pool`` path (https://github.com/ansible-collections/community.routeros/pull/327).
release_summary: Bugfix and feature release.
fragments:
- 3.1.0.yml
- 324-fix-firewall-log-and-log-prefix.yaml
- 327-add-missing-ip-pool-fields.yml
release_date: '2024-12-02'
3.2.0:
changes:
minor_changes:
- api_info, api_modify - add support for the ``routing filter community-list``
path implemented by RouterOS 7 and newer (https://github.com/ansible-collections/community.routeros/pull/331).
release_summary: Feature release.
fragments:
- 3.2.0.yml
- 331-add-routing-filter-community-list.yml
release_date: '2024-12-30'
3.3.0:
changes:
minor_changes:
- api_info, api_modify - add missing attribute ``require-message-auth`` for
the ``radius`` path which exists since RouterOS version 7.15 (https://github.com/ansible-collections/community.routeros/issues/338,
https://github.com/ansible-collections/community.routeros/pull/339).
- api_info, api_modify - add the ``interface 6to4`` path. Used to manage IPv6
tunnels via tunnel-brokers like HE, where native IPv6 is not provided (https://github.com/ansible-collections/community.routeros/pull/342).
- api_info, api_modify - add the ``interface wireless access-list`` and ``interface
wireless connect-list`` paths (https://github.com/ansible-collections/community.routeros/issues/284,
https://github.com/ansible-collections/community.routeros/pull/340).
- api_info, api_modify - add the ``use-interface-duid`` option for ``ipv6
dhcp-client`` path. This option prevents issues with Fritzbox modems and
routers, when using virtual interfaces (like VLANs) may create duplicated
records in hosts config, this breaks original "expose-host" function. Also
add the ``script``, ``custom-duid`` and ``validate-server-duid`` as backport
from 7.15 version update (https://github.com/ansible-collections/community.routeros/pull/341).
release_summary: Feature release.
fragments:
- 3.3.0.yml
- 339-add-require-message-auth-for-radius.yml
- 340-add-interface-wireless-access-and-connect-list.yml
- 341-add-dhcpv6-client-use-interface-duid.yml
- 342-add-interface-6to4.yml
release_date: '2025-01-27'
3.4.0:
changes:
bugfixes:
- api_info, api_modify - remove the primary key ``action`` from the ``interface
wifi provisioning`` path, since RouterOS also allows to create completely
duplicate entries (https://github.com/ansible-collections/community.routeros/issues/344,
https://github.com/ansible-collections/community.routeros/pull/345).
minor_changes:
- api_info, api_modify - add support for the ``ip dns forwarders`` path implemented
by RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/343).
release_summary: Feature and bugfix release.
fragments:
- 3.4.0.yml
- 343-add-ip-dns-forwarders.yml
- 345-interface-wifi-provisioning.yml
release_date: '2025-02-24'
3.5.0:
changes:
minor_changes:
- api_info, api_modify - change default for ``/ip/cloud/ddns-enabled`` for
RouterOS 7.17 and newer from ``yes`` to ``auto`` (https://github.com/ansible-collections/community.routeros/pull/350).
release_summary: Feature release.
fragments:
- 3.5.0.yml
- 350-ip-cloud-ddns-enabled-auto.yml
release_date: '2025-03-22'
3.6.0:
changes:
minor_changes:
- api_info, api_modify - add ``mdns-repeat-ifaces`` to ``ip dns`` for RouterOS
7.16 and newer (https://github.com/ansible-collections/community.routeros/pull/358).
- api_info, api_modify - field name change in ``routing bgp connection`` path
implemented by RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/360).
- api_info, api_modify - rename ``is-responder`` property in ``interface wireguard
peers`` to ``responder`` for RouterOS 7.17 and newer (https://github.com/ansible-collections/community.routeros/pull/364).
release_summary: Feature release.
fragments:
- 3.6.0.yml
- 358-mdns-repeat-ifaces.yml
- 360-bgp-connection-afi.yml
- 364-wireguard-responder.yml
release_date: '2025-04-21'
3.7.0:
changes:
minor_changes:
- api_find_and_modify - allow to control whether ``dynamic`` and/or ``builtin``
entries are ignored with the new ``ignore_dynamic`` and ``ignore_builtin``
options (https://github.com/ansible-collections/community.routeros/issues/372,
https://github.com/ansible-collections/community.routeros/pull/373).
- api_info, api_modify - add ``port-cost-mode`` to ``interface bridge`` which
is supported since RouterOS 7.13 (https://github.com/ansible-collections/community.routeros/pull/371).
release_summary: Feature release.
fragments:
- 3.7.0.yml
- 371-add-bridge-port-cost-mode.yml
- 373-api_find_and_modify-dynamic-builtin.yml
release_date: '2025-05-31'
3.8.0:
changes:
minor_changes:
- api_info, api_modify - add ``interface ethernet switch port-isolation``
which is supported since RouterOS 6.43 (https://github.com/ansible-collections/community.routeros/pull/375).
- 'api_info, api_modify - add ``routing bfd configuration``. Officially stabilized
BFD support for BGP and OSPF is available since RouterOS 7.11
(https://github.com/ansible-collections/community.routeros/pull/375).
'
- api_modify, api_info - support API path ``ip ipsec mode-config`` (https://github.com/ansible-collections/community.routeros/pull/376).
release_summary: Feature release.
fragments:
- 3.8.0.yml
- 375-port_isolation-and-routing_bfd_configuration.yml
- 376-ipsec-mode-config.yml
release_date: '2025-06-14'
3.8.1:
changes:
bugfixes:
- facts and api_facts modules - prevent deprecation warnings when used with
ansible-core 2.19 (https://github.com/ansible-collections/community.routeros/pull/384).
release_summary: Bugfix release.
fragments:
- 3.8.1.yml
- 384-warnings.yml
release_date: '2025-07-26'

View file

@ -7,9 +7,9 @@ changelog_filename_template: ../CHANGELOG.rst
changelog_filename_version_depth: 0
changes_file: changelog.yaml
changes_format: combined
ignore_other_fragment_extensions: true
keep_fragments: false
mention_ancestor: true
flatmap: true
new_plugins_after_name: removed_features
notesdir: fragments
output_formats:
@ -40,3 +40,4 @@ use_fqcn: true
add_plugin_period: true
changelog_nice_yaml: true
changelog_sort: version
vcs: auto

View file

@ -0,0 +1 @@
release_summary: Feature release.

View file

@ -0,0 +1,3 @@
minor_changes:
- api_info, api_modify - add ``disable-link-local-address`` and ``stale-neighbor-timeout`` fields to ``ipv6 settings`` (https://github.com/ansible-collections/community.routeros/pull/380).
- api_info, api_modify - adjust neighbor limit fields in ``ipv6 settings`` to match RouterOS 7.18 and newer (https://github.com/ansible-collections/community.routeros/pull/380).

View file

@ -0,0 +1,2 @@
minor_changes:
- api_info, api modify - add ``remote-log-format``, ``remote-protocol``, and ``event-delimiter`` to ``system logging action`` (https://github.com/ansible-collections/community.routeros/pull/381).

View file

@ -0,0 +1,2 @@
minor_changes:
- api_info, api_modify - set ``passthrough`` default in ``ip firewall mangle`` to ``true`` for RouterOS 7.19 and newer (https://github.com/ansible-collections/community.routeros/pull/382).

View file

@ -0,0 +1,7 @@
---
minor_changes:
- api_info, api_modify - since RouterOS 7.17 VRF is supported for OVPN server.
It now supports multiple entries, while ``api_modify`` so far only accepted a single entry.
The ``interface ovpn-server server`` path now allows multiple entries
on RouterOS 7.17 and newer
(https://github.com/ansible-collections/community.routeros/pull/383).

View file

@ -57,7 +57,7 @@ This results in the following output:
}
PLAY RECAP *******************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Check out the documentation of the :ansplugin:`community.routeros.api module <community.routeros.api#module>` for details on the options.
@ -191,7 +191,7 @@ When this playbook completed successfully, you should be able to use the HTTPS a
.. code-block:: yaml+jinja
- community.routeros.api:
...
# ...
tls: true
validate_certs: true
validate_cert_hostname: true

View file

@ -66,22 +66,22 @@ With the above inventory, you can use the following playbook to execute ``/syste
gather_facts: false
tasks:
- name: Gather system resources
community.routeros.command:
commands:
- /system resource print
register: system_resource_print
- name: Gather system resources
community.routeros.command:
commands:
- /system resource print
register: system_resource_print
- name: Show system resources
debug:
var: system_resource_print.stdout_lines
- name: Show system resources
debug:
var: system_resource_print.stdout_lines
- name: Gather facts
community.routeros.facts:
- name: Gather facts
community.routeros.facts:
- name: Show a fact
debug:
msg: "First IP address: {{ ansible_net_all_ipv4_addresses[0] }}"
- name: Show a fact
debug:
msg: "First IP address: {{ ansible_net_all_ipv4_addresses[0] }}"
This results in the following output:
@ -126,4 +126,4 @@ This results in the following output:
}
PLAY RECAP *******************************************************************************************************
router : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
router : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

View file

@ -7,7 +7,7 @@
namespace: community
name: routeros
version: 3.0.0
version: 3.9.0
readme: README.md
authors:
- Egor Zaitsev (github.com/heuels)
@ -16,7 +16,7 @@ authors:
description: Modules and plugins for MikroTik RouterOS
license:
- GPL-3.0-or-later
#license_file: COPYING
# license_file: COPYING
tags:
- network
- mikrotik

53
noxfile.py Normal file
View file

@ -0,0 +1,53 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# The following metadata allows Python runners and nox to install the required
# dependencies for running this Python script:
#
# /// script
# dependencies = ["nox>=2025.02.09", "antsibull-nox"]
# ///
import os
import sys
import nox
# We try to import antsibull-nox, and if that doesn't work, provide a more useful
# error message to the user.
try:
import antsibull_nox
except ImportError:
print("You need to install antsibull-nox in the same Python environment as nox.")
sys.exit(1)
IN_CI = os.environ.get("CI") == "true"
antsibull_nox.load_antsibull_nox_toml()
@nox.session(name="update-docs", default=True)
def update_docs_fragments(session: nox.Session) -> None:
"""
Update/check auto-generated parts of docs fragments.
"""
session.install("ansible-core")
prepare = antsibull_nox.sessions.prepare_collections(
session, install_in_site_packages=True
)
if not prepare:
return
data = ["python", "tests/update-docs.py"]
if IN_CI:
data.append("--lint")
session.run(*data)
# Allow to run the noxfile with `python noxfile.py`, `pipx run noxfile.py`, or similar.
# Requires nox >= 2025.02.09
if __name__ == "__main__":
nox.main()

View file

@ -5,15 +5,14 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
author: "Egor Zaitsev (@heuels)"
name: routeros
short_description: Use routeros cliconf to run command on MikroTik RouterOS platform
description:
- This routeros plugin provides low level abstraction apis for
sending and receiving CLI commands from MikroTik RouterOS network devices.
'''
- This routeros plugin provides low level abstraction APIs for sending and receiving CLI commands from MikroTik RouterOS
network devices.
"""
import re
import json

View file

@ -10,7 +10,7 @@ __metaclass__ = type
class ModuleDocFragment(object):
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options:
hostname:
description:
@ -43,17 +43,16 @@ options:
- ssl
port:
description:
- RouterOS api port. If O(tls) is set, port will apply to TLS/SSL connection.
- RouterOS API port. If O(tls) is set, port will apply to TLS/SSL connection.
- Defaults are V(8728) for the HTTP API, and V(8729) for the HTTPS API.
type: int
force_no_cert:
description:
- Set to V(true) to connect without a certificate when O(tls=true).
- See also O(validate_certs).
- B(Note:) this forces the use of anonymous Diffie-Hellman (ADH) ciphers. The protocol is susceptible
to Man-in-the-Middle attacks, because the keys used in the exchange are not authenticated.
Instead of simply connecting without a certificate to "make things work" have a look at
O(validate_certs) and O(ca_path).
- B(Note:) this forces the use of anonymous Diffie-Hellman (ADH) ciphers. The protocol is susceptible to Man-in-the-Middle
attacks, because the keys used in the exchange are not authenticated. Instead of simply connecting without a certificate
to "make things work" have a look at O(validate_certs) and O(ca_path).
type: bool
default: false
version_added: 2.4.0
@ -61,10 +60,9 @@ options:
description:
- Set to V(false) to skip validation of TLS certificates.
- See also O(validate_cert_hostname). Only used when O(tls=true).
- B(Note:) instead of simply deactivating certificate validations to "make things work",
please consider creating your own CA certificate and using it to sign certificates used
for your router. You can tell the module about your CA certificate with the O(ca_path)
option.
- B(Note:) instead of simply deactivating certificate validations to "make things work", please consider creating your
own CA certificate and using it to sign certificates used for your router. You can tell the module about your CA certificate
with the O(ca_path) option.
type: bool
default: true
version_added: 1.2.0
@ -93,10 +91,10 @@ requirements:
- Python >= 3.6 (for librouteros)
seealso:
- ref: ansible_collections.community.routeros.docsite.api-guide
description: How to connect to RouterOS devices with the RouterOS API
'''
description: How to connect to RouterOS devices with the RouterOS API.
"""
RESTRICT = r'''
RESTRICT = r"""
options:
restrict:
type: list
@ -115,24 +113,21 @@ options:
values:
description:
- The values of the field to limit to.
- >-
Note that the types of the values are important. If you provide a string V("0"),
and librouteros converts the value returned by the API to the integer V(0),
then this will not match. If you are not sure, better include both variants:
both the string and the integer.
- 'Note that the types of the values are important. If you provide a string V("0"), and librouteros converts the
value returned by the API to the integer V(0), then this will not match. If you are not sure, better include both
variants: both the string and the integer.'
type: list
elements: raw
regex:
description:
- A regular expression matching values of the field to limit to.
- Note that all values will be converted to strings before matching.
- It is not possible to match disabled values with regular expressions.
Set O(restrict[].match_disabled=true) if you also want to match disabled values.
- It is not possible to match disabled values with regular expressions. Set O(restrict[].match_disabled=true) if
you also want to match disabled values.
type: str
invert:
description:
- Invert the condition. This affects O(restrict[].match_disabled), O(restrict[].values),
and O(restrict[].regex).
- Invert the condition. This affects O(restrict[].match_disabled), O(restrict[].values), and O(restrict[].regex).
type: bool
default: false
'''
"""

View file

@ -11,88 +11,102 @@ __metaclass__ = type
class ModuleDocFragment(object):
# Standard documentation fragment
DOCUMENTATION = r'''
DOCUMENTATION = r"""
options: {}
attributes:
check_mode:
description: Can run in C(check_mode) and return changed status prediction without modifying target.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
platform:
description: Target OS/families that can be operated against.
support: N/A
'''
check_mode:
description: Can run in C(check_mode) and return changed status prediction without modifying target.
diff_mode:
description: Will return details on what has changed (or possibly needs changing in C(check_mode)), when in diff mode.
platform:
description: Target OS/families that can be operated against.
support: N/A
idempotent:
description:
- When run twice in a row outside check mode, with the same arguments, the second invocation indicates no change.
- This assumes that the system controlled/queried by the module has not changed in a relevant way.
"""
# Should be used together with the standard fragment
IDEMPOTENT_NOT_MODIFY_STATE = r"""
options: {}
attributes:
idempotent:
support: full
details:
- This action does not modify state.
"""
# Should be used together with the standard fragment
INFO_MODULE = r'''
options: {}
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
'''
ACTIONGROUP_API = r'''
options: {}
attributes:
action_group:
description: Use C(group/community.routeros.api) in C(module_defaults) to set defaults for this module.
support: full
membership:
- community.routeros.api
action_group:
description: Use C(group/community.routeros.api) in C(module_defaults) to set defaults for this module.
support: full
membership:
- community.routeros.api
'''
CONN = r'''
CONN = r"""
options: {}
attributes:
become:
description: Is usable alongside C(become) keywords.
connection:
description: Uses the target's configured connection information to execute code on it.
delegation:
description: Can be used in conjunction with C(delegate_to) and related keywords.
'''
become:
description: Is usable alongside C(become) keywords.
connection:
description: Uses the target's configured connection information to execute code on it.
delegation:
description: Can be used in conjunction with C(delegate_to) and related keywords.
"""
FACTS = r'''
FACTS = r"""
options: {}
attributes:
facts:
description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
'''
facts:
description: Action returns an C(ansible_facts) dictionary that will update existing host facts.
"""
# Should be used together with the standard fragment and the FACTS fragment
FACTS_MODULE = r'''
options: {}
attributes:
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
facts:
support: full
check_mode:
support: full
details:
- This action does not modify state.
diff_mode:
support: N/A
details:
- This action does not modify state.
facts:
support: full
'''
FILES = r'''
FILES = r"""
options: {}
attributes:
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
'''
safe_file_operations:
description: Uses Ansible's strict file operation functions to ensure proper permissions and avoid data corruption.
"""
FLOW = r'''
FLOW = r"""
options: {}
attributes:
action:
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
async:
description: Supports being used with the C(async) keyword.
'''
action:
description: Indicates this has a corresponding action plugin so some parts of the options can be executed on the controller.
async:
description: Supports being used with the C(async) keyword.
"""

View file

@ -20,6 +20,7 @@ DOCUMENTATION:
- Felix Fontein (@felixfontein)
EXAMPLES: |
---
- name: Join arguments for a RouterOS CLI command
ansible.builtin.set_fact:
arguments: "{{ ['foo=bar', 'comment=foo is bar'] | community.routeros.join }}"

View file

@ -30,6 +30,7 @@ DOCUMENTATION:
- Felix Fontein (@felixfontein)
EXAMPLES: |
---
- name: Convert a list to a dictionary
ansible.builtin.set_fact:
dictionary: "{{ ['foo=bar', 'comment=foo is bar'] | community.routeros.list_to_dict }}"

View file

@ -19,9 +19,11 @@ DOCUMENTATION:
- Felix Fontein (@felixfontein)
EXAMPLES: |
---
- name: Quote a RouterOS CLI command argument
ansible.builtin.set_fact:
quoted: "{{ 'comment=this is a "comment"' | community.routeros.quote_argument }}"
quoted: >-
{{ 'comment=this is a "comment"' | community.routeros.quote_argument }}
# Should result in 'comment="this is a \"comment\""'
RETURN:

View file

@ -19,9 +19,11 @@ DOCUMENTATION:
- Felix Fontein (@felixfontein)
EXAMPLES: |
---
- name: Quote a RouterOS CLI command argument's value
ansible.builtin.set_fact:
quoted: "{{ 'this is a "comment"' | community.routeros.quote_argument_value }}"
quoted: >-
{{ 'this is a "comment"' | community.routeros.quote_argument_value }}
# Should result in '"this is a \"comment\""'
RETURN:

View file

@ -19,9 +19,11 @@ DOCUMENTATION:
- Felix Fontein (@felixfontein)
EXAMPLES: |
---
- name: Split command into list of arguments
ansible.builtin.set_fact:
argument_list: "{{ 'foo=bar comment="foo is bar" baz' | community.routeros.split }}"
argument_list: >-
{{ 'foo=bar comment="foo is bar" baz' | community.routeros.split }}
# Should result in ['foo=bar', 'comment=foo is bar', 'baz']
RETURN:

View file

@ -226,6 +226,25 @@ def join_path(path):
# 3. All bold attributes go into the `primary_keys` list -- this is not always true!
PATHS = {
('interface', '6to4'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
primary_keys=('name', ),
fields={
'clamp-tcp-mss': KeyInfo(default=True),
'comment': KeyInfo(can_disable=True, remove_value=''),
'disabled': KeyInfo(default=False),
'dont-fragment': KeyInfo(default=False),
'dscp': KeyInfo(default='inherit'),
'ipsec-secret': KeyInfo(can_disable=True),
'keepalive': KeyInfo(default='10s,10', can_disable=True),
'local-address': KeyInfo(default='0.0.0.0'),
'mtu': KeyInfo(default='auto'),
'name': KeyInfo(),
'remote-address': KeyInfo(required=True),
}
),
),
('interface', 'bonding'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
@ -262,6 +281,7 @@ PATHS = {
versioned_fields=[
([('7.0', '<')], 'ingress-filtering', KeyInfo(default=False)),
([('7.0', '>=')], 'ingress-filtering', KeyInfo(default=True)),
([('7.13', '>=')], 'port-cost-mode', KeyInfo(default='long')),
([('7.16', '>=')], 'forward-reserved-addresses', KeyInfo(default=False)),
([('7.16', '>=')], 'max-learned-entries', KeyInfo(default='auto')),
],
@ -632,13 +652,22 @@ PATHS = {
),
('ip', 'ipsec', 'mode-config'): APIData(
unversioned=VersionedAPIData(
unknown_mechanism=True,
# primary_keys=('default', ),
fully_understood=True,
primary_keys=('name', ),
versioned_fields=[
([('6.43', '>=')], 'responder', KeyInfo(default=False)),
([('6.44', '>=')], 'address', KeyInfo(can_disable=True, remove_value='0.0.0.0')),
],
fields={
'default': KeyInfo(),
'address-pool': KeyInfo(can_disable=True, remove_value='none'),
'address-prefix-length': KeyInfo(),
'comment': KeyInfo(can_disable=True, remove_value=''),
'name': KeyInfo(),
'responder': KeyInfo(),
'use-responder-dns': KeyInfo(),
'split-dns': KeyInfo(can_disable=True, remove_value=''),
'split-include': KeyInfo(can_disable=True, remove_value=''),
'src-address-list': KeyInfo(can_disable=True, remove_value=''),
'static-dns': KeyInfo(can_disable=True, remove_value=''),
'system-dns': KeyInfo(default=False),
},
),
),
@ -708,7 +737,9 @@ PATHS = {
fully_understood=True,
primary_keys=('name', ),
fields={
'comment': KeyInfo(),
'name': KeyInfo(),
'next-pool': KeyInfo(),
'ranges': KeyInfo(),
},
),
@ -893,6 +924,20 @@ PATHS = {
)),
],
),
('routing', 'filter', 'community-list'): APIData(
versioned=[
('7', '>=', VersionedAPIData(
fully_understood=True,
fields={
'list': KeyInfo(required=True),
'comment': KeyInfo(can_disable=True, remove_value=''),
'disabled': KeyInfo(can_disable=True),
'communities': KeyInfo(can_disable=True),
'regexp': KeyInfo(can_disable=True),
},
)),
],
),
('routing', 'ospf', 'instance'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
@ -1508,13 +1553,19 @@ PATHS = {
fully_understood=True,
versioned_fields=[
([('7.16', '>=')], 'multipath-hash-policy', KeyInfo(default='l3')),
([('7.17', '>=')], 'disable-link-local-address', KeyInfo(default=False)),
([('7.17', '>=')], 'stale-neighbor-timeout', KeyInfo(default=60)),
([('7.18', '>=')], 'allow-fast-path', KeyInfo(default=True)),
([('7.18', '<')], 'max-neighbor-entries', KeyInfo(default=8192)),
([('7.18', '>=')], 'min-neighbor-entries', KeyInfo()),
([('7.18', '>=')], 'soft-max-neighbor-entries', KeyInfo()),
([('7.18', '>=')], 'max-neighbor-entries', KeyInfo()),
],
fields={
'accept-redirects': KeyInfo(default='yes-if-forwarding-disabled'),
'accept-router-advertisements': KeyInfo(default='yes-if-forwarding-disabled'),
'disable-ipv6': KeyInfo(default=False),
'forward': KeyInfo(default=True),
'max-neighbor-entries': KeyInfo(default=8192),
},
),
),
@ -1613,23 +1664,46 @@ PATHS = {
),
),
('interface', 'ovpn-server', 'server'): APIData(
unversioned=VersionedAPIData(
single_value=True,
fully_understood=True,
fields={
'auth': KeyInfo(),
'cipher': KeyInfo(),
'default-profile': KeyInfo(default='default'),
'enabled': KeyInfo(default=False),
'keepalive-timeout': KeyInfo(default=60),
'mac-address': KeyInfo(),
'max-mtu': KeyInfo(default=1500),
'mode': KeyInfo(default='ip'),
'netmask': KeyInfo(default=24),
'port': KeyInfo(default=1194),
'require-client-certificate': KeyInfo(default=False),
},
),
versioned=[
('7.17', '>=', VersionedAPIData(
fully_understood=True,
fields={
'auth': KeyInfo(),
'cipher': KeyInfo(),
'default-profile': KeyInfo(default='default'),
'enabled': KeyInfo(default=False),
'keepalive-timeout': KeyInfo(default=60),
'mac-address': KeyInfo(),
'max-mtu': KeyInfo(default=1500),
'mode': KeyInfo(default='ip'),
'name': KeyInfo(default=''),
'netmask': KeyInfo(default=24),
'port': KeyInfo(default=1194),
'protocol': KeyInfo(default='tcp'),
'require-client-certificate': KeyInfo(default=False),
'vrf': KeyInfo(default='main'),
},
)),
('7.17', '<', VersionedAPIData(
single_value=True,
fully_understood=True,
fields={
'auth': KeyInfo(),
'cipher': KeyInfo(),
'default-profile': KeyInfo(default='default'),
'enabled': KeyInfo(default=False),
'keepalive-timeout': KeyInfo(default=60),
'mac-address': KeyInfo(),
'max-mtu': KeyInfo(default=1500),
'mode': KeyInfo(default='ip'),
'name': KeyInfo(default=''),
'netmask': KeyInfo(default=24),
'port': KeyInfo(default=1194),
'protocol': KeyInfo(default='tcp'),
'require-client-certificate': KeyInfo(default=False),
},
))
]
),
('interface', 'pppoe-server', 'server'): APIData(
unversioned=VersionedAPIData(
@ -2002,7 +2076,6 @@ PATHS = {
versioned=[
('7.13', '>=', VersionedAPIData(
fully_understood=True,
primary_keys=('action', ),
fields={
'action': KeyInfo(default='none'),
'address-ranges': KeyInfo(can_disable=True),
@ -2407,7 +2480,8 @@ PATHS = {
},
versioned_fields=[
([('7.15', '>=')], 'name', KeyInfo()),
([('7.15', '>=')], 'is-responder', KeyInfo()),
([('7.15', '>='), ('7.17', '<')], 'is-responder', KeyInfo()),
([('7.17', '>=')], 'responder', KeyInfo()),
],
),
),
@ -2529,6 +2603,30 @@ PATHS = {
},
),
),
('interface', 'wireless', 'access-list'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
fields={
'allow-signal-out-of-range': KeyInfo(default='10s'),
'ap-tx-limit': KeyInfo(default=0),
'authentication': KeyInfo(default=True),
'client-tx-limit': KeyInfo(default=0),
'comment': KeyInfo(can_disable=True, remove_value=''),
'disabled': KeyInfo(default=False),
'forwarding': KeyInfo(default=True),
'interface': KeyInfo(default='any'),
'mac-address': KeyInfo(default='00:00:00:00:00:00'),
'management-protection-key': KeyInfo(default=''),
'private-algo': KeyInfo(default='none'),
'private-key': KeyInfo(default=''),
'private-pre-shared-key': KeyInfo(default=''),
'signal-range': KeyInfo(default='-120..120'),
'time': KeyInfo(),
'vlan-id': KeyInfo(default=1),
'vlan-mode': KeyInfo(default='default'),
},
),
),
('interface', 'wireless', 'cap'): APIData(
unversioned=VersionedAPIData(
single_value=True,
@ -2547,6 +2645,41 @@ PATHS = {
},
),
),
('interface', 'wireless', 'connect-list'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
fields={
'3gpp': KeyInfo(default=''),
'allow-signal-out-of-range': KeyInfo(default='10s'),
'area-prefix': KeyInfo(default=''),
'comment': KeyInfo(can_disable=True, remove_value=''),
'connect': KeyInfo(default=True),
'disabled': KeyInfo(default=False),
'interface': KeyInfo(required=True),
'interworking': KeyInfo(default='any'),
'iw-asra': KeyInfo(default='any'),
'iw-authentication-types': KeyInfo(),
'iw-connection-capabilities': KeyInfo(),
'iw-esr': KeyInfo(default='any'),
'iw-hessid': KeyInfo(default='00:00:00:00:00:00'),
'iw-hotspot20': KeyInfo(default='any'),
'iw-hotspot20-dgaf': KeyInfo(default='any'),
'iw-internet': KeyInfo(default='any'),
'iw-ipv4-availability': KeyInfo(default='any'),
'iw-ipv6-availability': KeyInfo(default='any'),
'iw-network-type': KeyInfo(default='wildcard'),
'iw-realms': KeyInfo(),
'iw-roaming-ois': KeyInfo(default=''),
'iw-uesa': KeyInfo(default='any'),
'iw-venue': KeyInfo(default='any'),
'mac-address': KeyInfo(default='00:00:00:00:00:00'),
'security-profile': KeyInfo(default='none'),
'signal-range': KeyInfo(default='-120..120'),
'ssid': KeyInfo(default=''),
'wireless-protocol': KeyInfo(default='any'),
},
),
),
('interface', 'wireless', 'security-profiles'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
@ -2684,8 +2817,11 @@ PATHS = {
unversioned=VersionedAPIData(
single_value=True,
fully_understood=True,
versioned_fields=[
([('7.17', '<')], 'ddns-enabled', KeyInfo(default=False)),
([('7.17', '>=')], 'ddns-enabled', KeyInfo(default='auto')),
],
fields={
'ddns-enabled': KeyInfo(default=False),
'ddns-update-interval': KeyInfo(default='none'),
'update-time': KeyInfo(default=True),
},
@ -2871,6 +3007,7 @@ PATHS = {
([('7.8', '>=')], 'doh-max-concurrent-queries', KeyInfo(default=50)),
([('7.8', '>=')], 'doh-max-server-connections', KeyInfo(default=5)),
([('7.8', '>=')], 'doh-timeout', KeyInfo(default='5s')),
([('7.16', '>=')], 'mdns-repeat-ifaces', KeyInfo()),
],
fields={
'allow-remote-requests': KeyInfo(),
@ -2903,6 +3040,22 @@ PATHS = {
)),
],
),
('ip', 'dns', 'forwarders'): APIData(
versioned=[
('7.17', '>=', VersionedAPIData(
fully_understood=True,
required_one_of=[['dns-servers', 'doh-servers']],
fields={
'comment': KeyInfo(can_disable=True, remove_value=''),
'disabled': KeyInfo(default=False),
'dns-servers': KeyInfo(default=''),
'doh-servers': KeyInfo(default=''),
'name': KeyInfo(required=True),
'verify-doh-cert': KeyInfo(default=True),
},
)),
],
),
('ip', 'dns', 'static'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
@ -2984,8 +3137,8 @@ PATHS = {
'jump-target': KeyInfo(can_disable=True),
'layer7-protocol': KeyInfo(can_disable=True),
'limit': KeyInfo(can_disable=True),
'log': KeyInfo(can_disable=True),
'log-prefix': KeyInfo(can_disable=True),
'log': KeyInfo(default=False),
'log-prefix': KeyInfo(default=''),
'nth': KeyInfo(can_disable=True),
'out-bridge-port': KeyInfo(can_disable=True),
'out-bridge-port-list': KeyInfo(can_disable=True),
@ -3021,6 +3174,10 @@ PATHS = {
unversioned=VersionedAPIData(
fully_understood=True,
stratify_keys=('chain', ),
versioned_fields=[
([('7.19', '<')], 'passthrough', KeyInfo(can_disable=True)),
([('7.19', '>=')], 'passthrough', KeyInfo(default=True)),
],
fields={
'action': KeyInfo(),
'address-list': KeyInfo(can_disable=True),
@ -3055,8 +3212,8 @@ PATHS = {
'jump-target': KeyInfo(can_disable=True),
'layer7-protocol': KeyInfo(can_disable=True),
'limit': KeyInfo(can_disable=True),
'log': KeyInfo(can_disable=True),
'log-prefix': KeyInfo(can_disable=True),
'log': KeyInfo(default=False),
'log-prefix': KeyInfo(default=''),
'new-connection-mark': KeyInfo(can_disable=True),
'new-dscp': KeyInfo(can_disable=True),
'new-mss': KeyInfo(can_disable=True),
@ -3072,7 +3229,6 @@ PATHS = {
'p2p': KeyInfo(can_disable=True),
'packet-mark': KeyInfo(can_disable=True),
'packet-size': KeyInfo(can_disable=True),
'passthrough': KeyInfo(can_disable=True),
'per-connection-classifier': KeyInfo(can_disable=True),
'port': KeyInfo(can_disable=True),
'priority': KeyInfo(can_disable=True),
@ -3135,8 +3291,8 @@ PATHS = {
'jump-target': KeyInfo(can_disable=True),
'layer7-protocol': KeyInfo(can_disable=True),
'limit': KeyInfo(can_disable=True),
'log': KeyInfo(can_disable=True),
'log-prefix': KeyInfo(can_disable=True),
'log': KeyInfo(default=False),
'log-prefix': KeyInfo(default=''),
'nth': KeyInfo(can_disable=True),
'out-bridge-port': KeyInfo(can_disable=True),
'out-bridge-port-list': KeyInfo(can_disable=True),
@ -3198,8 +3354,8 @@ PATHS = {
'ipv4-options': KeyInfo(can_disable=True),
'jump-target': KeyInfo(can_disable=True),
'limit': KeyInfo(can_disable=True),
'log': KeyInfo(can_disable=True),
'log-prefix': KeyInfo(can_disable=True),
'log': KeyInfo(default=False),
'log-prefix': KeyInfo(default=''),
'nth': KeyInfo(can_disable=True),
'out-bridge-port': KeyInfo(can_disable=True),
'out-bridge-port-list': KeyInfo(can_disable=True),
@ -3469,6 +3625,14 @@ PATHS = {
'request': KeyInfo(),
'use-peer-dns': KeyInfo(default=True),
},
versioned_fields=[
# Mikrotik does not provide exact version in official changelogs.
# The 7.15 version is the earliest, found option in router config backups:
([('7.15', '>=')], 'script', KeyInfo(default='')),
([('7.15', '>=')], 'custom-duid', KeyInfo(default='')),
([('7.15', '>=')], 'use-interface-duid', KeyInfo(default=False)),
([('7.15', '>=')], 'validate-server-duid', KeyInfo(default=True)),
],
),
),
('ipv6', 'dhcp-server'): APIData(
@ -4002,6 +4166,9 @@ PATHS = {
'src-address': KeyInfo(default='0.0.0.0'),
'timeout': KeyInfo(default='300ms'),
},
versioned_fields=[
([('7.15', '>=')], 'require-message-auth', KeyInfo(default='yes-for-request-resp')),
],
),
),
('radius', 'incoming'): APIData(
@ -4052,6 +4219,28 @@ PATHS = {
},
),
),
('routing', 'bfd', 'configuration'): APIData(
versioned=[
('7.11', '>=', VersionedAPIData(
fully_understood=True,
fields={
'address-list': KeyInfo(),
'addresses': KeyInfo(),
'comment': KeyInfo(can_disable=True, remove_value=''),
'copy-from': KeyInfo(),
'disabled': KeyInfo(default=False),
'forbid-bfd': KeyInfo(),
'interfaces': KeyInfo(),
'min-echo-rx': KeyInfo(),
'min-rx': KeyInfo(),
'min-tx': KeyInfo(),
'multiplier': KeyInfo(),
'place-before': KeyInfo(),
'vrf': KeyInfo(),
},
))
],
),
('routing', 'bfd', 'interface'): APIData(
unversioned=VersionedAPIData(
unknown_mechanism=True,
@ -4768,6 +4957,18 @@ PATHS = {
},
),
),
('interface', 'ethernet', 'switch', 'port-isolation'): APIData(
versioned=[
('6.43', '>=', VersionedAPIData(
primary_keys=('name', ),
fully_understood=True,
fields={
'forwarding-override': KeyInfo(),
'name': KeyInfo(),
},
)),
],
),
('ip', 'dhcp-client', 'option'): APIData(
unversioned=VersionedAPIData(
fixed_entries=True,
@ -4858,10 +5059,13 @@ PATHS = {
('routing', 'bgp', 'connection'): APIData(
unversioned=VersionedAPIData(
fully_understood=True,
versioned_fields=[
([('7.19', '<')], 'address-families', KeyInfo()),
([('7.19', '>=')], 'afi', KeyInfo()),
],
fields={
'as': KeyInfo(),
'add-path-out': KeyInfo(),
'address-families': KeyInfo(),
'cisco-vpls-nlri-len-fmt': KeyInfo(),
'cluster-id': KeyInfo(),
'comment': KeyInfo(),
@ -5040,6 +5244,11 @@ PATHS = {
unversioned=VersionedAPIData(
fully_understood=True,
primary_keys=('name',),
versioned_fields=[
([('7.18', '>=')], 'remote-log-format', KeyInfo(default='default')),
([('7.18', '>=')], 'remote-protocol', KeyInfo(default='udp')),
([('7.18', '>=')], 'cef-event-delimiter', KeyInfo(default='\r\n')),
],
fields={
'bsd-syslog': KeyInfo(default=False),
'comment': KeyInfo(can_disable=True, remove_value=''),
@ -5142,7 +5351,7 @@ PATHS = {
'protocol': KeyInfo(default='all'),
'src-address': KeyInfo(),
'src-port': KeyInfo(default='any'),
# The template field can't really be changed once the item is
# The template field ca not really be changed once the item is
# created. This config captures the behavior best as it can
# i.e. template=yes is shown, template=no is hidden.
'template': KeyInfo(can_disable=True, remove_value=False),

View file

@ -77,7 +77,7 @@ def _ros_api_connect(module, username, password, host, port, use_tls, force_no_c
elif not validate_cert_hostname:
ctx.check_hostname = False
else:
# Since librouteros doesn't pass server_hostname,
# Since librouteros does not pass server_hostname,
# we have to do this ourselves:
def wrap_context(*args, **kwargs):
kwargs.pop('server_hostname', None)

View file

@ -8,19 +8,17 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: api
author: "Nikolay Dachev (@NikolayDachev)"
short_description: Ansible module for RouterOS API
description:
- Ansible module for RouterOS API with the Python C(librouteros) library.
- This module can add, remove, update, query and execute arbitrary command in RouterOS via API.
- This module can add, remove, update, query, and execute arbitrary command in RouterOS through the API.
notes:
- O(add), O(remove), O(update), O(cmd), and O(query) are mutually exclusive.
- Use the M(community.routeros.api_modify) and M(community.routeros.api_find_and_modify) modules
for more specific modifications, and the M(community.routeros.api_info) module for a more controlled
way of returning all entries for a path.
- Use the M(community.routeros.api_modify) and M(community.routeros.api_find_and_modify) modules for more specific modifications,
and the M(community.routeros.api_info) module for a more controlled way of returning all entries for a path.
extends_documentation_fragment:
- community.routeros.api
- community.routeros.attributes
@ -35,11 +33,15 @@ attributes:
platforms: RouterOS
action_group:
version_added: 2.1.0
idempotent:
support: N/A
details:
- Whether the executed command is idempotent depends on the operation performed.
options:
path:
description:
- Main path for all other arguments.
- If other arguments are not set, api will return all items in selected path.
- If other arguments are not set, the module will return all items in selected path.
- Example V(ip address). Equivalent of RouterOS CLI C(/ip address print).
required: true
type: str
@ -59,20 +61,21 @@ options:
update:
description:
- Update config/value in RouterOS by '.id' in selected path.
- Example V(.id=*03 address=1.1.1.3/32) and path V(ip address) will replace existing ip address with C(.id=*03).
- Example V(.id=*03 address=1.1.1.3/32) and path V(ip address) will replace the existing IP address with C(.id=*03).
- Equivalent in RouterOS CLI C(/ip address set address=1.1.1.3/32 numbers=1).
- Note C(number) in RouterOS CLI is different from C(.id).
type: str
query:
description:
- Query given path for selected query attributes from RouterOS aip.
- Query given path for selected query attributes from RouterOS API.
- WHERE is key word which extend query. WHERE format is key operator value - with spaces.
- WHERE valid operators are V(==) or V(eq), V(!=) or V(not), V(>) or V(more), V(<) or V(less).
- Example path V(ip address) and query V(.id address) will return only C(.id) and C(address) for all items in V(ip address) path.
- Example path V(ip address) and query V(.id address WHERE address == 1.1.1.3/32).
will return only C(.id) and C(address) for items in V(ip address) path, where address is eq to 1.1.1.3/32.
- Example path V(interface) and query V(mtu name WHERE mut > 1400) will
return only interfaces C(mtu,name) where mtu is bigger than 1400.
- Example path V(ip address) and query V(.id address) will return only C(.id) and C(address) for all items in V(ip address)
path.
- Example path V(ip address) and query V(.id address WHERE address == 1.1.1.3/32). will return only C(.id) and C(address)
for items in V(ip address) path, where address is eq to 1.1.1.3/32.
- Example path V(interface) and query V(mtu name WHERE mut > 1400) will return only interfaces C(mtu,name) where mtu
is bigger than 1400.
- Equivalent in RouterOS CLI C(/interface print where mtu > 1400).
type: str
extended_query:
@ -91,7 +94,8 @@ options:
where:
description:
- Allows to restrict the objects returned.
- The conditions here must all match. An O(extended_query.where[].or) condition needs at least one of its conditions to match.
- The conditions here must all match. An O(extended_query.where[].or) condition needs at least one of its conditions
to match.
type: list
elements: dict
suboptions:
@ -105,7 +109,8 @@ options:
description:
- The operator to use for matching.
- For equality use V(==) or V(eq). For less use V(<) or V(less). For more use V(>) or V(more).
- Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].value) must be a list.
- Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].value) must
be a list.
- Either O(extended_query.where[].or) or all of O(extended_query.where[].attribute), O(extended_query.where[].is),
and O(extended_query.where[].value) have to be specified.
type: str
@ -133,7 +138,8 @@ options:
description:
- The operator to use for matching.
- For equality use V(==) or V(eq). For less use V(<) or V(less). For more use V(>) or V(more).
- Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].or[].value) must be a list.
- Use V(in) to check whether the value is part of a list. In that case, O(extended_query.where[].or[].value)
must be a list.
type: str
choices: ["==", "!=", ">", "<", "in", "eq", "not", "more", "less"]
required: true
@ -150,14 +156,15 @@ options:
type: str
seealso:
- ref: ansible_collections.community.routeros.docsite.quoting
description: How to quote and unquote commands and arguments
description: How to quote and unquote commands and arguments.
- module: community.routeros.api_facts
- module: community.routeros.api_find_and_modify
- module: community.routeros.api_info
- module: community.routeros.api_modify
'''
"""
EXAMPLES = '''
EXAMPLES = r"""
---
- name: Get example - ip address print
community.routeros.api:
hostname: "{{ hostname }}"
@ -216,8 +223,8 @@ EXAMPLES = '''
- attribute: "network"
is: "in"
value:
- "10.20.36.0"
- "192.168.255.0"
- "10.20.36.0"
- "192.168.255.0"
register: extended_queryout
- name: Dump "Extended query example" output
@ -231,9 +238,9 @@ EXAMPLES = '''
username: "{{ username }}"
path: "ip address"
update: >-
.id=*14
address=192.168.255.20/24
comment={{ 'Update 192.168.255.10/24 to 192.168.255.20/24 on ether2' | community.routeros.quote_argument_value }}
.id=*14
address=192.168.255.20/24
comment={{ 'Update 192.168.255.10/24 to 192.168.255.20/24 on ether2' | community.routeros.quote_argument_value }}
- name: Remove example - ether2 ip 192.168.255.20/24 with ".id = *14"
community.routeros.api:
@ -255,18 +262,17 @@ EXAMPLES = '''
- name: Dump "Arbitrary command example" output
ansible.builtin.debug:
msg: '{{ arbitraryout }}'
'''
"""
RETURN = '''
---
RETURN = r"""
message:
description: All outputs are in list with dictionary elements returned from RouterOS api.
sample:
- address: 1.2.3.4
- address: 2.3.4.5
type: list
returned: always
'''
description: All outputs are in list with dictionary elements returned from RouterOS API.
sample:
- address: 1.2.3.4
- address: 2.3.4.5
type: list
returned: always
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native

View file

@ -9,29 +9,27 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: api_facts
author:
- "Egor Zaitsev (@heuels)"
- "Nikolay Dachev (@NikolayDachev)"
- "Felix Fontein (@felixfontein)"
- "Egor Zaitsev (@heuels)"
- "Nikolay Dachev (@NikolayDachev)"
- "Felix Fontein (@felixfontein)"
version_added: 2.1.0
short_description: Collect facts from remote devices running MikroTik RouterOS using the API
description:
- Collects a base set of device facts from a remote device that
is running RouterOS. This module prepends all of the
base network fact keys with C(ansible_net_<fact>). The facts
module will always collect a base set of facts from the device
- Collects a base set of device facts from a remote device that is running RouterOS. This module prepends all of the base
network fact keys with C(ansible_net_<fact>). The facts module will always collect a base set of facts from the device
and can enable or disable collection of additional facts.
- As opposed to the M(community.routeros.facts) module, it uses the
RouterOS API, similar to the M(community.routeros.api) module.
- As opposed to the M(community.routeros.facts) module, it uses the RouterOS API, similar to the M(community.routeros.api)
module.
extends_documentation_fragment:
- community.routeros.api
- community.routeros.attributes
- community.routeros.attributes.actiongroup_api
- community.routeros.attributes.facts
- community.routeros.attributes.facts_module
- community.routeros.attributes.idempotent_not_modify_state
attributes:
platform:
support: full
@ -39,12 +37,10 @@ attributes:
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
V(all), V(hardware), V(interfaces), and V(routing).
- Can specify a list of values to include a larger subset.
Values can also be used with an initial V(!) to specify that a
specific subset should not be collected.
- When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument
include V(all), V(hardware), V(interfaces), and V(routing).
- Can specify a list of values to include a larger subset. Values can also be used with an initial V(!) to specify that
a specific subset should not be collected.
required: false
default:
- all
@ -56,9 +52,10 @@ seealso:
- module: community.routeros.api_find_and_modify
- module: community.routeros.api_info
- module: community.routeros.api_modify
'''
"""
EXAMPLES = """
EXAMPLES = r"""
---
- name: Collect all facts from the device
community.routeros.api_facts:
hostname: 192.168.88.1
@ -75,7 +72,7 @@ EXAMPLES = """
- "!hardware"
"""
RETURN = """
RETURN = r"""
ansible_facts:
description: "Dictionary of IP geolocation facts for a host's IP address."
returned: always
@ -422,8 +419,6 @@ FACT_SUBSETS = dict(
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
warnings = []
def main():
argument_spec = dict(
@ -488,7 +483,7 @@ def main():
key = 'ansible_net_%s' % key
ansible_facts[key] = value
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
module.exit_json(ansible_facts=ansible_facts)
if __name__ == '__main__':

View file

@ -8,8 +8,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: api_find_and_modify
author:
- "Felix Fontein (@felixfontein)"
@ -17,13 +16,13 @@ short_description: Find and modify information using the API
version_added: 2.1.0
description:
- Allows to find entries for a path by conditions and modify the values of these entries.
- Use the M(community.routeros.api_find_and_modify) module to set all entries of a path to specific values,
or change multiple entries in different ways in one step.
- Use the M(community.routeros.api_find_and_modify) module to set all entries of a path to specific values, or change multiple
entries in different ways in one step.
notes:
- "If you want to change values based on their old values (like change all comments 'foo' to 'bar') and make sure that
there are at least N such values, you can use O(require_matches_min=N) together with O(allow_no_matches=true).
This will make the module fail if there are less than N such entries, but not if there is no match. The latter case
is needed for idempotency of the task: once the values have been changed, there should be no further match."
- "If you want to change values based on their old values (like change all comments 'foo' to 'bar') and make sure that there
are at least N such values, you can use O(require_matches_min=N) together with O(allow_no_matches=true). This will make
the module fail if there are less than N such entries, but not if there is no match. The latter case is needed for idempotency
of the task: once the values have been changed, there should be no further match."
extends_documentation_fragment:
- community.routeros.api
- community.routeros.attributes
@ -36,6 +35,8 @@ attributes:
platform:
support: full
platforms: RouterOS
idempotent:
support: full
options:
path:
description:
@ -74,14 +75,30 @@ options:
- Whether to allow that no match is found.
- If not specified, this value is induced from whether O(require_matches_min) is 0 or larger.
type: bool
ignore_dynamic:
description:
- Whether to ignore dynamic entries.
- By default, they are considered. If set to V(true), they are not considered.
- It is generally recommended to set this to V(true) unless when you really need to modify dynamic entries.
type: bool
default: false
version_added: 3.7.0
ignore_builtin:
description:
- Whether to ignore builtin entries.
- By default, they are considered. If set to V(true), they are not considered.
- It is generally recommended to set this to V(true) unless when you really need to modify builtin entries.
type: bool
default: false
version_added: 3.7.0
seealso:
- module: community.routeros.api
- module: community.routeros.api_facts
- module: community.routeros.api_modify
- module: community.routeros.api_info
'''
"""
EXAMPLES = '''
EXAMPLES = r"""
---
- name: Rename bridge from 'bridge' to 'my-bridge'
community.routeros.api_find_and_modify:
@ -93,6 +110,10 @@ EXAMPLES = '''
name: bridge
values:
name: my-bridge
# Always ignore dynamic and builtin entries
# (not relevant for this path, but generally recommended)
ignore_dynamic: true
ignore_builtin: true
- name: Change IP address to 192.168.1.1 for interface bridge - assuming there is only one
community.routeros.api_find_and_modify:
@ -108,55 +129,58 @@ EXAMPLES = '''
# exactly one is configured.
require_matches_min: 1
require_matches_max: 1
'''
# Always ignore dynamic and builtin entries
# (not relevant for this path, but generally recommended)
ignore_dynamic: true
ignore_builtin: true
"""
RETURN = '''
---
RETURN = r"""
old_data:
description:
- A list of all elements for the current path before a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: success
description:
- A list of all elements for the current path before a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: success
new_data:
description:
- A list of all elements for the current path after a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.1.1/24"
comment: awesome
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.1.0
type: list
elements: dict
returned: success
description:
- A list of all elements for the current path after a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.1.1/24"
comment: awesome
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.1.0
type: list
elements: dict
returned: success
match_count:
description:
- The number of entries that matched the criteria in O(find).
sample: 1
type: int
returned: success
description:
- The number of entries that matched the criteria in O(find).
sample: 1
type: int
returned: success
modify__count:
description:
- The number of entries that were modified.
sample: 1
type: int
returned: success
'''
description:
- The number of entries that were modified.
sample: 1
type: int
returned: success
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native
@ -185,6 +209,17 @@ def compose_api_path(api, path):
return api_path
def filter_entries(entries, ignore_dynamic=False, ignore_builtin=False):
result = []
for entry in entries:
if ignore_dynamic and entry.get('dynamic', False):
continue
if ignore_builtin and entry.get('builtin', False):
continue
result.append(entry)
return result
DISABLED_MEANS_EMPTY_STRING = ('comment', )
@ -196,6 +231,8 @@ def main():
require_matches_min=dict(type='int', default=0),
require_matches_max=dict(type='int'),
allow_no_matches=dict(type='bool'),
ignore_dynamic=dict(type='bool', default=False),
ignore_builtin=dict(type='bool', default=False),
)
module_args.update(api_argument_spec())
@ -223,6 +260,9 @@ def main():
if key in values:
module.fail_json(msg='`values` must not contain both "{key}" and "!{key}"!'.format(key=key))
ignore_dynamic = module.params['ignore_dynamic']
ignore_builtin = module.params['ignore_builtin']
check_has_library(module)
api = create_api(module)
@ -230,7 +270,7 @@ def main():
api_path = compose_api_path(api, path)
old_data = list(api_path)
old_data = filter_entries(list(api_path), ignore_dynamic=ignore_dynamic, ignore_builtin=ignore_builtin)
new_data = [entry.copy() for entry in old_data]
# Find matching entries
@ -299,7 +339,7 @@ def main():
error=to_native(e),
)
)
new_data = list(api_path)
new_data = filter_entries(list(api_path), ignore_dynamic=ignore_dynamic, ignore_builtin=ignore_builtin)
# Produce return value
more = {}

View file

@ -8,8 +8,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: api_info
author:
- "Felix Fontein (@felixfontein)"
@ -18,17 +17,18 @@ version_added: 2.2.0
description:
- Allows to retrieve information for a path using the API.
- This can be used to backup a path to restore it with the M(community.routeros.api_modify) module.
- Entries are normalized, dynamic and builtin entries are not returned. Use the O(handle_disabled) and
O(hide_defaults) options to control normalization, the O(include_dynamic) and O(include_builtin) options to also return
dynamic resp. builtin entries, and use O(unfiltered) to return all fields including counters.
- B(Note) that this module is still heavily in development, and only supports B(some) paths.
If you want to support new paths, or think you found problems with existing paths, please first
L(create an issue in the community.routeros Issue Tracker,https://github.com/ansible-collections/community.routeros/issues/).
- Entries are normalized, dynamic and builtin entries are not returned. Use the O(handle_disabled) and O(hide_defaults)
options to control normalization, the O(include_dynamic) and O(include_builtin) options to also return dynamic resp. builtin
entries, and use O(unfiltered) to return all fields including counters.
- B(Note) that this module is still heavily in development, and only supports B(some) paths. If you want to support new
paths, or think you found problems with existing paths, please first L(create an issue in the community.routeros Issue
Tracker,https://github.com/ansible-collections/community.routeros/issues/).
extends_documentation_fragment:
- community.routeros.api
- community.routeros.api.restrict
- community.routeros.attributes
- community.routeros.attributes.actiongroup_api
- community.routeros.attributes.idempotent_not_modify_state
- community.routeros.attributes.info_module
attributes:
platform:
@ -43,224 +43,232 @@ options:
type: str
choices:
# BEGIN PATH LIST
- caps-man aaa
- caps-man access-list
- caps-man channel
- caps-man configuration
- caps-man datapath
- caps-man manager
- caps-man manager interface
- caps-man provisioning
- caps-man security
- certificate settings
- interface bonding
- interface bridge
- interface bridge mlag
- interface bridge port
- interface bridge port-controller
- interface bridge port-extender
- interface bridge settings
- interface bridge vlan
- interface detect-internet
- interface eoip
- interface ethernet
- interface ethernet poe
- interface ethernet switch
- interface ethernet switch port
- interface gre
- interface gre6
- interface l2tp-client
- interface l2tp-server server
- interface list
- interface list member
- interface ovpn-client
- interface ovpn-server server
- interface ppp-client
- interface pppoe-client
- interface pppoe-server server
- interface pptp-server server
- interface sstp-server server
- interface vlan
- interface vrrp
- interface wifi
- interface wifi aaa
- interface wifi access-list
- interface wifi cap
- interface wifi capsman
- interface wifi channel
- interface wifi configuration
- interface wifi datapath
- interface wifi interworking
- interface wifi provisioning
- interface wifi security
- interface wifi steering
- interface wifiwave2
- interface wifiwave2 aaa
- interface wifiwave2 access-list
- interface wifiwave2 cap
- interface wifiwave2 capsman
- interface wifiwave2 channel
- interface wifiwave2 configuration
- interface wifiwave2 datapath
- interface wifiwave2 interworking
- interface wifiwave2 provisioning
- interface wifiwave2 security
- interface wifiwave2 steering
- interface wireguard
- interface wireguard peers
- interface wireless
- interface wireless align
- interface wireless cap
- interface wireless security-profiles
- interface wireless sniffer
- interface wireless snooper
- iot modbus
- ip accounting
- ip accounting web-access
- ip address
- ip arp
- ip cloud
- ip cloud advanced
- ip dhcp-client
- ip dhcp-client option
- ip dhcp-relay
- ip dhcp-server
- ip dhcp-server config
- ip dhcp-server lease
- ip dhcp-server matcher
- ip dhcp-server network
- ip dhcp-server option
- ip dhcp-server option sets
- ip dns
- ip dns adlist
- ip dns static
- ip firewall address-list
- ip firewall connection tracking
- ip firewall filter
- ip firewall layer7-protocol
- ip firewall mangle
- ip firewall nat
- ip firewall raw
- ip firewall service-port
- ip hotspot service-port
- ip ipsec identity
- ip ipsec peer
- ip ipsec policy
- ip ipsec profile
- ip ipsec proposal
- ip ipsec settings
- ip neighbor discovery-settings
- ip pool
- ip proxy
- ip route
- ip route rule
- ip route vrf
- ip service
- ip settings
- ip smb
- ip socks
- ip ssh
- ip tftp settings
- ip traffic-flow
- ip traffic-flow ipfix
- ip traffic-flow target
- ip upnp
- ip upnp interfaces
- ip vrf
- ipv6 address
- ipv6 dhcp-client
- ipv6 dhcp-server
- ipv6 dhcp-server option
- ipv6 firewall address-list
- ipv6 firewall filter
- ipv6 firewall mangle
- ipv6 firewall nat
- ipv6 firewall raw
- ipv6 nd
- ipv6 nd prefix
- ipv6 nd prefix default
- ipv6 route
- ipv6 settings
- mpls
- mpls interface
- mpls ldp
- mpls ldp accept-filter
- mpls ldp advertise-filter
- mpls ldp interface
- port firmware
- port remote-access
- ppp aaa
- ppp profile
- ppp secret
- queue interface
- queue simple
- queue tree
- queue type
- radius
- radius incoming
- routing bgp aggregate
- routing bgp connection
- routing bgp instance
- routing bgp network
- routing bgp peer
- routing bgp template
- routing filter
- routing filter num-list
- routing filter rule
- routing filter select-rule
- routing id
- routing igmp-proxy
- routing igmp-proxy interface
- routing mme
- routing ospf area
- routing ospf area range
- routing ospf instance
- routing ospf interface-template
- routing ospf static-neighbor
- routing pimsm instance
- routing pimsm interface-template
- routing rip
- routing ripng
- routing rule
- routing table
- snmp
- snmp community
- system clock
- system clock manual
- system health settings
- system identity
- system leds settings
- system logging
- system logging action
- system note
- system ntp client
- system ntp client servers
- system ntp server
- system package update
- system resource irq rps
- system routerboard settings
- system scheduler
- system script
- system upgrade mirror
- system ups
- system watchdog
- tool bandwidth-server
- tool e-mail
- tool graphing
- tool graphing interface
- tool graphing resource
- tool mac-server
- tool mac-server mac-winbox
- tool mac-server ping
- tool netwatch
- tool romon
- tool sms
- tool sniffer
- tool traffic-generator
- user
- user aaa
- user group
- user settings
- caps-man aaa
- caps-man access-list
- caps-man channel
- caps-man configuration
- caps-man datapath
- caps-man manager
- caps-man manager interface
- caps-man provisioning
- caps-man security
- certificate settings
- interface 6to4
- interface bonding
- interface bridge
- interface bridge mlag
- interface bridge port
- interface bridge port-controller
- interface bridge port-extender
- interface bridge settings
- interface bridge vlan
- interface detect-internet
- interface eoip
- interface ethernet
- interface ethernet poe
- interface ethernet switch
- interface ethernet switch port
- interface ethernet switch port-isolation
- interface gre
- interface gre6
- interface l2tp-client
- interface l2tp-server server
- interface list
- interface list member
- interface ovpn-client
- interface ovpn-server server
- interface ppp-client
- interface pppoe-client
- interface pppoe-server server
- interface pptp-server server
- interface sstp-server server
- interface vlan
- interface vrrp
- interface wifi
- interface wifi aaa
- interface wifi access-list
- interface wifi cap
- interface wifi capsman
- interface wifi channel
- interface wifi configuration
- interface wifi datapath
- interface wifi interworking
- interface wifi provisioning
- interface wifi security
- interface wifi steering
- interface wifiwave2
- interface wifiwave2 aaa
- interface wifiwave2 access-list
- interface wifiwave2 cap
- interface wifiwave2 capsman
- interface wifiwave2 channel
- interface wifiwave2 configuration
- interface wifiwave2 datapath
- interface wifiwave2 interworking
- interface wifiwave2 provisioning
- interface wifiwave2 security
- interface wifiwave2 steering
- interface wireguard
- interface wireguard peers
- interface wireless
- interface wireless access-list
- interface wireless align
- interface wireless cap
- interface wireless connect-list
- interface wireless security-profiles
- interface wireless sniffer
- interface wireless snooper
- iot modbus
- ip accounting
- ip accounting web-access
- ip address
- ip arp
- ip cloud
- ip cloud advanced
- ip dhcp-client
- ip dhcp-client option
- ip dhcp-relay
- ip dhcp-server
- ip dhcp-server config
- ip dhcp-server lease
- ip dhcp-server matcher
- ip dhcp-server network
- ip dhcp-server option
- ip dhcp-server option sets
- ip dns
- ip dns adlist
- ip dns forwarders
- ip dns static
- ip firewall address-list
- ip firewall connection tracking
- ip firewall filter
- ip firewall layer7-protocol
- ip firewall mangle
- ip firewall nat
- ip firewall raw
- ip firewall service-port
- ip hotspot service-port
- ip ipsec identity
- ip ipsec mode-config
- ip ipsec peer
- ip ipsec policy
- ip ipsec profile
- ip ipsec proposal
- ip ipsec settings
- ip neighbor discovery-settings
- ip pool
- ip proxy
- ip route
- ip route rule
- ip route vrf
- ip service
- ip settings
- ip smb
- ip socks
- ip ssh
- ip tftp settings
- ip traffic-flow
- ip traffic-flow ipfix
- ip traffic-flow target
- ip upnp
- ip upnp interfaces
- ip vrf
- ipv6 address
- ipv6 dhcp-client
- ipv6 dhcp-server
- ipv6 dhcp-server option
- ipv6 firewall address-list
- ipv6 firewall filter
- ipv6 firewall mangle
- ipv6 firewall nat
- ipv6 firewall raw
- ipv6 nd
- ipv6 nd prefix
- ipv6 nd prefix default
- ipv6 route
- ipv6 settings
- mpls
- mpls interface
- mpls ldp
- mpls ldp accept-filter
- mpls ldp advertise-filter
- mpls ldp interface
- port firmware
- port remote-access
- ppp aaa
- ppp profile
- ppp secret
- queue interface
- queue simple
- queue tree
- queue type
- radius
- radius incoming
- routing bfd configuration
- routing bgp aggregate
- routing bgp connection
- routing bgp instance
- routing bgp network
- routing bgp peer
- routing bgp template
- routing filter
- routing filter community-list
- routing filter num-list
- routing filter rule
- routing filter select-rule
- routing id
- routing igmp-proxy
- routing igmp-proxy interface
- routing mme
- routing ospf area
- routing ospf area range
- routing ospf instance
- routing ospf interface-template
- routing ospf static-neighbor
- routing pimsm instance
- routing pimsm interface-template
- routing rip
- routing ripng
- routing rule
- routing table
- snmp
- snmp community
- system clock
- system clock manual
- system health settings
- system identity
- system leds settings
- system logging
- system logging action
- system note
- system ntp client
- system ntp client servers
- system ntp server
- system package update
- system resource irq rps
- system routerboard settings
- system scheduler
- system script
- system upgrade mirror
- system ups
- system watchdog
- tool bandwidth-server
- tool e-mail
- tool graphing
- tool graphing interface
- tool graphing resource
- tool mac-server
- tool mac-server mac-winbox
- tool mac-server ping
- tool netwatch
- tool romon
- tool sms
- tool sniffer
- tool traffic-generator
- user
- user aaa
- user group
- user settings
# END PATH LIST
unfiltered:
description:
@ -316,9 +324,9 @@ seealso:
- module: community.routeros.api_facts
- module: community.routeros.api_find_and_modify
- module: community.routeros.api_modify
'''
"""
EXAMPLES = '''
EXAMPLES = r"""
---
- name: Get IP addresses
community.routeros.api_info:
@ -343,26 +351,25 @@ EXAMPLES = '''
- name: Print data for IP addresses
ansible.builtin.debug:
var: ip_addresses.result
'''
"""
RETURN = '''
---
RETURN = r"""
result:
description: A list of all elements for the current path.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: always
'''
description: A list of all elements for the current path.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: always
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.common.text.converters import to_native

View file

@ -8,8 +8,7 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: api_modify
author:
- "Felix Fontein (@felixfontein)"
@ -17,17 +16,17 @@ short_description: Modify data at paths with API
version_added: 2.2.0
description:
- Allows to modify information for a path using the API.
- Use the M(community.routeros.api_find_and_modify) module to modify one or multiple entries in a controlled way
depending on some search conditions.
- Use the M(community.routeros.api_find_and_modify) module to modify one or multiple entries in a controlled way depending
on some search conditions.
- To make a backup of a path that can be restored with this module, use the M(community.routeros.api_info) module.
- The module ignores dynamic and builtin entries.
- B(Note) that this module is still heavily in development, and only supports B(some) paths.
If you want to support new paths, or think you found problems with existing paths, please first
L(create an issue in the community.routeros Issue Tracker,https://github.com/ansible-collections/community.routeros/issues/).
- B(Note) that this module is still heavily in development, and only supports B(some) paths. If you want to support new
paths, or think you found problems with existing paths, please first L(create an issue in the community.routeros Issue
Tracker,https://github.com/ansible-collections/community.routeros/issues/).
notes:
- If write-only fields are present in the path, the module is B(not idempotent) in a strict sense,
since it is not able to verify the current value of these fields. The behavior the module should
assume can be controlled with the O(handle_write_only) option.
- If write-only fields are present in the path, the module is B(not idempotent) in a strict sense, since it is not able
to verify the current value of these fields. The behavior the module should assume can be controlled with the O(handle_write_only)
option.
requirements:
- Needs L(ordereddict,https://pypi.org/project/ordereddict) for Python 2.6
extends_documentation_fragment:
@ -43,233 +42,244 @@ attributes:
platform:
support: full
platforms: RouterOS
idempotent:
support: full
options:
path:
description:
- Path to query.
- An example value is V(ip address). This is equivalent to running modification commands in C(/ip address) in the RouterOS CLI.
- An example value is V(ip address). This is equivalent to running modification commands in C(/ip address) in the RouterOS
CLI.
required: true
type: str
choices:
# BEGIN PATH LIST
- caps-man aaa
- caps-man access-list
- caps-man channel
- caps-man configuration
- caps-man datapath
- caps-man manager
- caps-man manager interface
- caps-man provisioning
- caps-man security
- certificate settings
- interface bonding
- interface bridge
- interface bridge mlag
- interface bridge port
- interface bridge port-controller
- interface bridge port-extender
- interface bridge settings
- interface bridge vlan
- interface detect-internet
- interface eoip
- interface ethernet
- interface ethernet poe
- interface ethernet switch
- interface ethernet switch port
- interface gre
- interface gre6
- interface l2tp-client
- interface l2tp-server server
- interface list
- interface list member
- interface ovpn-client
- interface ovpn-server server
- interface ppp-client
- interface pppoe-client
- interface pppoe-server server
- interface pptp-server server
- interface sstp-server server
- interface vlan
- interface vrrp
- interface wifi
- interface wifi aaa
- interface wifi access-list
- interface wifi cap
- interface wifi capsman
- interface wifi channel
- interface wifi configuration
- interface wifi datapath
- interface wifi interworking
- interface wifi provisioning
- interface wifi security
- interface wifi steering
- interface wifiwave2
- interface wifiwave2 aaa
- interface wifiwave2 access-list
- interface wifiwave2 cap
- interface wifiwave2 capsman
- interface wifiwave2 channel
- interface wifiwave2 configuration
- interface wifiwave2 datapath
- interface wifiwave2 interworking
- interface wifiwave2 provisioning
- interface wifiwave2 security
- interface wifiwave2 steering
- interface wireguard
- interface wireguard peers
- interface wireless
- interface wireless align
- interface wireless cap
- interface wireless security-profiles
- interface wireless sniffer
- interface wireless snooper
- iot modbus
- ip accounting
- ip accounting web-access
- ip address
- ip arp
- ip cloud
- ip cloud advanced
- ip dhcp-client
- ip dhcp-client option
- ip dhcp-relay
- ip dhcp-server
- ip dhcp-server config
- ip dhcp-server lease
- ip dhcp-server matcher
- ip dhcp-server network
- ip dhcp-server option
- ip dhcp-server option sets
- ip dns
- ip dns adlist
- ip dns static
- ip firewall address-list
- ip firewall connection tracking
- ip firewall filter
- ip firewall layer7-protocol
- ip firewall mangle
- ip firewall nat
- ip firewall raw
- ip firewall service-port
- ip hotspot service-port
- ip ipsec identity
- ip ipsec peer
- ip ipsec policy
- ip ipsec profile
- ip ipsec proposal
- ip ipsec settings
- ip neighbor discovery-settings
- ip pool
- ip proxy
- ip route
- ip route rule
- ip route vrf
- ip service
- ip settings
- ip smb
- ip socks
- ip ssh
- ip tftp settings
- ip traffic-flow
- ip traffic-flow ipfix
- ip traffic-flow target
- ip upnp
- ip upnp interfaces
- ip vrf
- ipv6 address
- ipv6 dhcp-client
- ipv6 dhcp-server
- ipv6 dhcp-server option
- ipv6 firewall address-list
- ipv6 firewall filter
- ipv6 firewall mangle
- ipv6 firewall nat
- ipv6 firewall raw
- ipv6 nd
- ipv6 nd prefix
- ipv6 nd prefix default
- ipv6 route
- ipv6 settings
- mpls
- mpls interface
- mpls ldp
- mpls ldp accept-filter
- mpls ldp advertise-filter
- mpls ldp interface
- port firmware
- port remote-access
- ppp aaa
- ppp profile
- ppp secret
- queue interface
- queue simple
- queue tree
- queue type
- radius
- radius incoming
- routing bgp aggregate
- routing bgp connection
- routing bgp instance
- routing bgp network
- routing bgp peer
- routing bgp template
- routing filter
- routing filter num-list
- routing filter rule
- routing filter select-rule
- routing id
- routing igmp-proxy
- routing igmp-proxy interface
- routing mme
- routing ospf area
- routing ospf area range
- routing ospf instance
- routing ospf interface-template
- routing ospf static-neighbor
- routing pimsm instance
- routing pimsm interface-template
- routing rip
- routing ripng
- routing rule
- routing table
- snmp
- snmp community
- system clock
- system clock manual
- system health settings
- system identity
- system leds settings
- system logging
- system logging action
- system note
- system ntp client
- system ntp client servers
- system ntp server
- system package update
- system resource irq rps
- system routerboard settings
- system scheduler
- system script
- system upgrade mirror
- system ups
- system watchdog
- tool bandwidth-server
- tool e-mail
- tool graphing
- tool graphing interface
- tool graphing resource
- tool mac-server
- tool mac-server mac-winbox
- tool mac-server ping
- tool netwatch
- tool romon
- tool sms
- tool sniffer
- tool traffic-generator
- user
- user aaa
- user group
- user settings
- caps-man aaa
- caps-man access-list
- caps-man channel
- caps-man configuration
- caps-man datapath
- caps-man manager
- caps-man manager interface
- caps-man provisioning
- caps-man security
- certificate settings
- interface 6to4
- interface bonding
- interface bridge
- interface bridge mlag
- interface bridge port
- interface bridge port-controller
- interface bridge port-extender
- interface bridge settings
- interface bridge vlan
- interface detect-internet
- interface eoip
- interface ethernet
- interface ethernet poe
- interface ethernet switch
- interface ethernet switch port
- interface ethernet switch port-isolation
- interface gre
- interface gre6
- interface l2tp-client
- interface l2tp-server server
- interface list
- interface list member
- interface ovpn-client
- interface ovpn-server server
- interface ppp-client
- interface pppoe-client
- interface pppoe-server server
- interface pptp-server server
- interface sstp-server server
- interface vlan
- interface vrrp
- interface wifi
- interface wifi aaa
- interface wifi access-list
- interface wifi cap
- interface wifi capsman
- interface wifi channel
- interface wifi configuration
- interface wifi datapath
- interface wifi interworking
- interface wifi provisioning
- interface wifi security
- interface wifi steering
- interface wifiwave2
- interface wifiwave2 aaa
- interface wifiwave2 access-list
- interface wifiwave2 cap
- interface wifiwave2 capsman
- interface wifiwave2 channel
- interface wifiwave2 configuration
- interface wifiwave2 datapath
- interface wifiwave2 interworking
- interface wifiwave2 provisioning
- interface wifiwave2 security
- interface wifiwave2 steering
- interface wireguard
- interface wireguard peers
- interface wireless
- interface wireless access-list
- interface wireless align
- interface wireless cap
- interface wireless connect-list
- interface wireless security-profiles
- interface wireless sniffer
- interface wireless snooper
- iot modbus
- ip accounting
- ip accounting web-access
- ip address
- ip arp
- ip cloud
- ip cloud advanced
- ip dhcp-client
- ip dhcp-client option
- ip dhcp-relay
- ip dhcp-server
- ip dhcp-server config
- ip dhcp-server lease
- ip dhcp-server matcher
- ip dhcp-server network
- ip dhcp-server option
- ip dhcp-server option sets
- ip dns
- ip dns adlist
- ip dns forwarders
- ip dns static
- ip firewall address-list
- ip firewall connection tracking
- ip firewall filter
- ip firewall layer7-protocol
- ip firewall mangle
- ip firewall nat
- ip firewall raw
- ip firewall service-port
- ip hotspot service-port
- ip ipsec identity
- ip ipsec mode-config
- ip ipsec peer
- ip ipsec policy
- ip ipsec profile
- ip ipsec proposal
- ip ipsec settings
- ip neighbor discovery-settings
- ip pool
- ip proxy
- ip route
- ip route rule
- ip route vrf
- ip service
- ip settings
- ip smb
- ip socks
- ip ssh
- ip tftp settings
- ip traffic-flow
- ip traffic-flow ipfix
- ip traffic-flow target
- ip upnp
- ip upnp interfaces
- ip vrf
- ipv6 address
- ipv6 dhcp-client
- ipv6 dhcp-server
- ipv6 dhcp-server option
- ipv6 firewall address-list
- ipv6 firewall filter
- ipv6 firewall mangle
- ipv6 firewall nat
- ipv6 firewall raw
- ipv6 nd
- ipv6 nd prefix
- ipv6 nd prefix default
- ipv6 route
- ipv6 settings
- mpls
- mpls interface
- mpls ldp
- mpls ldp accept-filter
- mpls ldp advertise-filter
- mpls ldp interface
- port firmware
- port remote-access
- ppp aaa
- ppp profile
- ppp secret
- queue interface
- queue simple
- queue tree
- queue type
- radius
- radius incoming
- routing bfd configuration
- routing bgp aggregate
- routing bgp connection
- routing bgp instance
- routing bgp network
- routing bgp peer
- routing bgp template
- routing filter
- routing filter community-list
- routing filter num-list
- routing filter rule
- routing filter select-rule
- routing id
- routing igmp-proxy
- routing igmp-proxy interface
- routing mme
- routing ospf area
- routing ospf area range
- routing ospf instance
- routing ospf interface-template
- routing ospf static-neighbor
- routing pimsm instance
- routing pimsm interface-template
- routing rip
- routing ripng
- routing rule
- routing table
- snmp
- snmp community
- system clock
- system clock manual
- system health settings
- system identity
- system leds settings
- system logging
- system logging action
- system note
- system ntp client
- system ntp client servers
- system ntp server
- system package update
- system resource irq rps
- system routerboard settings
- system scheduler
- system script
- system upgrade mirror
- system ups
- system watchdog
- tool bandwidth-server
- tool e-mail
- tool graphing
- tool graphing interface
- tool graphing resource
- tool mac-server
- tool mac-server mac-winbox
- tool mac-server ping
- tool netwatch
- tool romon
- tool sms
- tool sniffer
- tool traffic-generator
- user
- user aaa
- user group
- user settings
# END PATH LIST
data:
description:
@ -297,12 +307,12 @@ options:
default: ignore
handle_entries_content:
description:
- For a single entry in O(data), this describes how to handle fields that are not mentioned
in that entry, but appear in the actual config.
- For a single entry in O(data), this describes how to handle fields that are not mentioned in that entry, but appear
in the actual config.
- If V(ignore), they are not modified.
- If V(remove), they are removed. If at least one cannot be removed, the module will fail.
- If V(remove_as_much_as_possible), all that can be removed will be removed. The ones that
cannot be removed will be kept.
- If V(remove_as_much_as_possible), all that can be removed will be removed. The ones that cannot be removed will be
kept.
- Note that V(remove) and V(remove_as_much_as_possible) do not apply to write-only fields.
type: str
choices:
@ -314,8 +324,8 @@ options:
description:
- How to handle values passed in for read-only fields.
- If V(ignore), they are not passed to the API.
- If V(validate), the values are not passed for creation, and for updating they are compared to the value returned for the object.
If they differ, the module fails.
- If V(validate), the values are not passed for creation, and for updating they are compared to the value returned for
the object. If they differ, the module fails.
- If V(error), the module will fail if read-only fields are provided.
type: str
choices:
@ -328,9 +338,8 @@ options:
description:
- How to handle values passed in for write-only fields.
- If V(create_only), they are passed on creation, and ignored for updating.
- If V(always_update), they are always passed to the API. This means that if such a value is present,
the module will always result in C(changed) since there is no way to validate whether the value
actually changed.
- If V(always_update), they are always passed to the API. This means that if such a value is present, the module will
always result in C(changed) since there is no way to validate whether the value actually changed.
- If V(error), the module will fail if write-only fields are provided.
type: str
choices:
@ -342,20 +351,18 @@ options:
restrict:
description:
- Restrict operation to entries matching the following criteria.
- This can be useful together with O(handle_absent_entries=remove) to operate on a subset of
the values.
- For example, for O(path=ip firewall filter), you can set O(restrict[].field=chain) and
O(restrict[].values=input) to restrict operation to the input chain, and ignore the
forward and output chains.
- This can be useful together with O(handle_absent_entries=remove) to operate on a subset of the values.
- For example, for O(path=ip firewall filter), you can set O(restrict[].field=chain) and O(restrict[].values=input)
to restrict operation to the input chain, and ignore the forward and output chains.
version_added: 2.18.0
seealso:
- module: community.routeros.api
- module: community.routeros.api_facts
- module: community.routeros.api_find_and_modify
- module: community.routeros.api_info
'''
"""
EXAMPLES = '''
EXAMPLES = r"""
---
- name: Setup DHCP server networks
# Ensures that we have exactly two DHCP server networks (in the specified order)
@ -410,43 +417,42 @@ EXAMPLES = '''
data:
- action: drop
chain: input
'''
"""
RETURN = '''
---
RETURN = r"""
old_data:
description:
- A list of all elements for the current path before a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: always
description:
- A list of all elements for the current path before a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.88.1/24"
comment: defconf
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.88.0
type: list
elements: dict
returned: always
new_data:
description:
- A list of all elements for the current path after a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.1.1/24"
comment: awesome
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.1.0
type: list
elements: dict
returned: always
'''
description:
- A list of all elements for the current path after a change was made.
sample:
- '.id': '*1'
actual-interface: bridge
address: "192.168.1.1/24"
comment: awesome
disabled: false
dynamic: false
interface: bridge
invalid: false
network: 192.168.1.0
type: list
elements: dict
returned: always
"""
from collections import defaultdict

View file

@ -7,87 +7,77 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: command
author: "Egor Zaitsev (@heuels)"
short_description: Run commands on remote devices running MikroTik RouterOS
description:
- Sends arbitrary commands to an RouterOS node and returns the results
read from the device. This module includes an
argument that will cause the module to wait for a specific condition
before returning or timing out if the condition is not met.
- The module always indicates a (changed) status. You can use
R(the changed_when task property,override_the_changed_result) to determine
whether a command task actually resulted in a change or not.
- Sends arbitrary commands to an RouterOS node and returns the results read from the device. This module includes an argument
that will cause the module to wait for a specific condition before returning or timing out if the condition is not met.
- The module always indicates a (changed) status. You can use R(the changed_when task property,override_the_changed_result)
to determine whether a command task actually resulted in a change or not.
extends_documentation_fragment:
- community.routeros.attributes
attributes:
check_mode:
support: none
details:
- Before community.routeros 3.0.0, the module claimed to support check mode.
It simply executed the command in check mode.
- Before community.routeros 3.0.0, the module claimed to support check mode. It simply executed the command in check
mode.
diff_mode:
support: none
platform:
support: full
platforms: RouterOS
idempotent:
support: N/A
details:
- Whether the executed command is idempotent depends on the command.
options:
commands:
description:
- List of commands to send to the remote RouterOS device over the
configured provider. The resulting output from the command
is returned. If the O(wait_for) argument is provided, the
module is not returned until the condition is satisfied or
the number of retries has expired.
- List of commands to send to the remote RouterOS device over the configured provider. The resulting output from the
command is returned. If the O(wait_for) argument is provided, the module is not returned until the condition is satisfied
or the number of retries has expired.
required: true
type: list
elements: str
wait_for:
description:
- List of conditions to evaluate against the output of the
command. The task will wait for each condition to be true
before moving forward. If the conditional is not true
within the configured number of retries, the task fails.
See examples.
- List of conditions to evaluate against the output of the command. The task will wait for each condition to be true
before moving forward. If the conditional is not true within the configured number of retries, the task fails. See
examples.
type: list
elements: str
match:
description:
- The O(match) argument is used in conjunction with the
O(wait_for) argument to specify the match policy. Valid
values are V(all) or V(any). If the value is set to V(all)
then all conditionals in the wait_for must be satisfied. If
the value is set to V(any) then only one of the values must be
satisfied.
- The O(match) argument is used in conjunction with the O(wait_for) argument to specify the match policy. Valid values
are V(all) or V(any). If the value is set to V(all) then all conditionals in the wait_for must be satisfied. If the
value is set to V(any) then only one of the values must be satisfied.
default: all
choices: ['any', 'all']
type: str
retries:
description:
- Specifies the number of retries a command should by tried
before it is considered failed. The command is run on the
target device every retry and evaluated against the
O(wait_for) conditions.
- Specifies the number of retries a command should by tried before it is considered failed. The command is run on the
target device every retry and evaluated against the O(wait_for) conditions.
default: 10
type: int
interval:
description:
- Configures the interval in seconds to wait between retries
of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before
trying the command again.
- Configures the interval in seconds to wait between retries of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before trying the command again.
default: 1
type: int
seealso:
- ref: ansible_collections.community.routeros.docsite.ssh-guide
description: How to connect to RouterOS devices with SSH
description: How to connect to RouterOS devices with SSH.
- ref: ansible_collections.community.routeros.docsite.quoting
description: How to quote and unquote commands and arguments
'''
description: How to quote and unquote commands and arguments.
"""
EXAMPLES = """
EXAMPLES = r"""
---
- name: Run command on remote devices
community.routeros.command:
commands: /system routerboard print
@ -113,19 +103,19 @@ EXAMPLES = """
- result[1] contains ether1
"""
RETURN = """
RETURN = r"""
stdout:
description: The set of responses from the commands
description: The set of responses from the commands.
returned: always apart from low level errors (such as action plugin)
type: list
sample: ['...', '...']
stdout_lines:
description: The value of stdout split into a list
description: The value of stdout split into a list.
returned: always apart from low level errors (such as action plugin)
type: list
sample: [['...', '...'], ['...'], ['...']]
failed_conditions:
description: The list of conditionals that have failed
description: The list of conditionals that have failed.
returned: failed
type: list
sample: ['...', '...']

View file

@ -7,21 +7,19 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
---
DOCUMENTATION = r"""
module: facts
author: "Egor Zaitsev (@heuels)"
short_description: Collect facts from remote devices running MikroTik RouterOS
description:
- Collects a base set of device facts from a remote device that
is running RouterOS. This module prepends all of the
base network fact keys with C(ansible_net_<fact>). The facts
module will always collect a base set of facts from the device
- Collects a base set of device facts from a remote device that is running RouterOS. This module prepends all of the base
network fact keys with C(ansible_net_<fact>). The facts module will always collect a base set of facts from the device
and can enable or disable collection of additional facts.
extends_documentation_fragment:
- community.routeros.attributes
- community.routeros.attributes.facts
- community.routeros.attributes.facts_module
- community.routeros.attributes.idempotent_not_modify_state
attributes:
platform:
support: full
@ -29,12 +27,10 @@ attributes:
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
V(all), V(hardware), V(config), V(interfaces), and V(routing).
- Can specify a list of values to include a larger subset.
Values can also be used with an initial V(!) to specify that a
specific subset should not be collected.
- When supplied, this argument will restrict the facts collected to a given subset. Possible values for this argument
include V(all), V(hardware), V(config), V(interfaces), and V(routing).
- Can specify a list of values to include a larger subset. Values can also be used with an initial V(!) to specify that
a specific subset should not be collected.
required: false
default:
- '!config'
@ -42,10 +38,11 @@ options:
elements: str
seealso:
- ref: ansible_collections.community.routeros.docsite.ssh-guide
description: How to connect to RouterOS devices with SSH
'''
description: How to connect to RouterOS devices with SSH.
"""
EXAMPLES = """
EXAMPLES = r"""
---
- name: Collect all facts from the device
community.routeros.facts:
gather_subset: all
@ -61,7 +58,7 @@ EXAMPLES = """
- "!hardware"
"""
RETURN = """
RETURN = r"""
ansible_facts:
description: "Dictionary of IP geolocation facts for a host's IP address."
returned: always
@ -129,9 +126,9 @@ ansible_facts:
ansible_net_config_nonverbose:
description:
- The current active config from the device in minimal form.
- This value is idempotent in the sense that if the facts module is run twice and the device's config
was not changed between the runs, the value is identical. This is achieved by running C(/export)
and stripping the timestamp from the comment in the first line.
- This value is idempotent in the sense that if the facts module is run twice and the device's config was not changed
between the runs, the value is identical. This is achieved by running C(/export) and stripping the timestamp from
the comment in the first line.
returned: O(gather_subset) contains V(config)
type: str
version_added: 1.2.0
@ -592,8 +589,6 @@ FACT_SUBSETS = dict(
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
warnings = list()
def main():
"""main entry point for module execution
@ -656,7 +651,7 @@ def main():
key = 'ansible_net_%s' % key
ansible_facts[key] = value
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
module.exit_json(ansible_facts=ansible_facts)
if __name__ == '__main__':

View file

@ -22,7 +22,7 @@
assert:
that:
- >-
result.msg == "Unexpected end of string during escaped parameter"
"Unexpected end of string during escaped parameter" in result.msg
- name: "Test quote_argument filter"
assert:

View file

@ -4,4 +4,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
collections:
- ansible.netcommon
- ansible.netcommon

View file

@ -22,7 +22,7 @@
assert:
that:
- >-
result.msg == "Unexpected end of string during escaped parameter"
"Unexpected end of string during escaped parameter" in result.msg
- name: "Test quote_argument filter"
assert:

View file

@ -1,13 +0,0 @@
{
"include_symlinks": false,
"prefixes": [
"docs/docsite/",
"plugins/",
"roles/"
],
"output": "path-line-column-message",
"requirements": [
"ansible-core",
"antsibull-docs"
]
}

View file

@ -1,29 +0,0 @@
#!/usr/bin/env python
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
"""Check extra collection docs with antsibull-docs."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import sys
import subprocess
def main():
"""Main entry point."""
env = os.environ.copy()
suffix = ':{env}'.format(env=env["ANSIBLE_COLLECTIONS_PATH"]) if 'ANSIBLE_COLLECTIONS_PATH' in env else ''
env['ANSIBLE_COLLECTIONS_PATH'] = '{root}{suffix}'.format(root=os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd()))), suffix=suffix)
p = subprocess.run(
['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--skip-rstcheck', '.'],
env=env,
check=False,
)
if p.returncode not in (0, 3):
print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode))
if __name__ == '__main__':
main()

View file

@ -1,4 +0,0 @@
{
"include_symlinks": false,
"output": "path-message"
}

View file

@ -1,3 +0,0 @@
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: Ansible Project

View file

@ -1,110 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2022, Felix Fontein <felix@fontein.de>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
"""Prevent files without a correct license identifier from being added to the source tree."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import glob
import sys
def format_license_list(licenses):
if not licenses:
return '(empty)'
return ', '.join(['"%s"' % license for license in licenses])
def find_licenses(filename, relax=False):
spdx_license_identifiers = []
other_license_identifiers = []
has_copyright = False
try:
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.rstrip()
if 'Copyright ' in line:
has_copyright = True
if 'Copyright: ' in line:
print('%s: found copyright line with "Copyright:". Please remove the colon.' % (filename, ))
if 'SPDX-FileCopyrightText: ' in line:
has_copyright = True
idx = line.find('SPDX-License-Identifier: ')
if idx >= 0:
lic_id = line[idx + len('SPDX-License-Identifier: '):]
spdx_license_identifiers.extend(lic_id.split(' OR '))
if 'GNU General Public License' in line:
if 'v3.0+' in line:
other_license_identifiers.append('GPL-3.0-or-later')
if 'version 3 or later' in line:
other_license_identifiers.append('GPL-3.0-or-later')
if 'Simplified BSD License' in line:
other_license_identifiers.append('BSD-2-Clause')
if 'Apache License 2.0' in line:
other_license_identifiers.append('Apache-2.0')
if 'PSF License' in line or 'Python-2.0' in line:
other_license_identifiers.append('PSF-2.0')
if 'MIT License' in line:
other_license_identifiers.append('MIT')
except Exception as exc:
print('%s: error while processing file: %s' % (filename, exc))
if len(set(spdx_license_identifiers)) < len(spdx_license_identifiers):
print('%s: found identical SPDX-License-Identifier values' % (filename, ))
if other_license_identifiers and set(other_license_identifiers) != set(spdx_license_identifiers):
print('%s: SPDX-License-Identifier yielded the license list %s, while manual guessing yielded the license list %s' % (
filename, format_license_list(spdx_license_identifiers), format_license_list(other_license_identifiers)))
if not has_copyright and not relax:
print('%s: found no copyright notice' % (filename, ))
return sorted(spdx_license_identifiers)
def main():
"""Main entry point."""
paths = sys.argv[1:] or sys.stdin.read().splitlines()
# The following paths are allowed to have no license identifier
no_comments_allowed = [
'changelogs/fragments/*.yml',
'changelogs/fragments/*.yaml',
]
# These files are completely ignored
ignore_paths = [
'.ansible-test-timeout.json',
'.reuse/dep5',
'LICENSES/*.txt',
'COPYING',
]
no_comments_allowed = [fn for pattern in no_comments_allowed for fn in glob.glob(pattern)]
ignore_paths = [fn for pattern in ignore_paths for fn in glob.glob(pattern)]
valid_licenses = [license_file[len('LICENSES/'):-len('.txt')] for license_file in glob.glob('LICENSES/*.txt')]
for path in paths:
if path.startswith('./'):
path = path[2:]
if path in ignore_paths or path.startswith('tests/output/'):
continue
if os.stat(path).st_size == 0:
continue
if not path.endswith('.license') and os.path.exists(path + '.license'):
path = path + '.license'
valid_licenses_for_path = valid_licenses
if path.startswith('plugins/') and not path.startswith(('plugins/modules/', 'plugins/module_utils/')):
valid_licenses_for_path = [license for license in valid_licenses if license == 'GPL-3.0-or-later']
licenses = find_licenses(path, relax=path in no_comments_allowed)
if not licenses:
if path not in no_comments_allowed:
print('%s: must have at least one license' % (path, ))
else:
for license in licenses:
if license not in valid_licenses_for_path:
print('%s: found not allowed license "%s", must be one of %s' % (
path, license, format_license_list(valid_licenses_for_path)))
if __name__ == '__main__':
main()

View file

@ -1,3 +0,0 @@
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: 2022, Felix Fontein <felix@fontein.de>

View file

@ -1,7 +0,0 @@
{
"include_symlinks": true,
"prefixes": [
"plugins/"
],
"output": "path-message"
}

View file

@ -1,3 +0,0 @@
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: Ansible Project

View file

@ -1,58 +0,0 @@
#!/usr/bin/env python
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
"""Prevent unwanted files from being added to the source tree."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import os.path
import sys
def main():
"""Main entry point."""
paths = sys.argv[1:] or sys.stdin.read().splitlines()
allowed_extensions = (
'.cs',
'.ps1',
'.psm1',
'.py',
)
skip_paths = set([
])
skip_directories = (
)
yaml_directories = (
'plugins/test/',
'plugins/filter/',
)
for path in paths:
if path in skip_paths:
continue
if any(path.startswith(skip_directory) for skip_directory in skip_directories):
continue
if os.path.islink(path):
print('%s: is a symbolic link' % (path, ))
elif not os.path.isfile(path):
print('%s: is not a regular file' % (path, ))
ext = os.path.splitext(path)[1]
if ext in ('.yml', ) and any(path.startswith(yaml_directory) for yaml_directory in yaml_directories):
continue
if ext not in allowed_extensions:
print('%s: extension must be one of: %s' % (path, ', '.join(allowed_extensions)))
if __name__ == '__main__':
main()

View file

@ -1,11 +0,0 @@
{
"include_symlinks": false,
"prefixes": [
"docs/docsite/rst/api-guide.rst",
"plugins/modules/"
],
"output": "path-line-column-message",
"requirements": [
"ansible-core"
]
}

View file

@ -1,3 +0,0 @@
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-FileCopyrightText: Ansible Project

View file

@ -1,21 +0,0 @@
#!/usr/bin/env python
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
"""Check whether update-docs.py modifies something."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import sys
import subprocess
def main():
"""Main entry point."""
p = subprocess.run([sys.executable, 'update-docs.py'], check=False)
if p.returncode not in (0, 1):
print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode))
if __name__ == '__main__':
main()

View file

@ -1,9 +1,9 @@
docs/docsite/rst/api-guide.rst rstcheck
docs/docsite/rst/quoting.rst rstcheck
docs/docsite/rst/ssh-guide.rst rstcheck
update-docs.py compile-2.6
update-docs.py compile-2.7
update-docs.py compile-3.5
update-docs.py future-import-boilerplate
update-docs.py metaclass-boilerplate
update-docs.py shebang
tests/update-docs.py compile-2.6
tests/update-docs.py compile-2.7
tests/update-docs.py compile-3.5
tests/update-docs.py future-import-boilerplate
tests/update-docs.py metaclass-boilerplate
tests/update-docs.py shebang

View file

@ -1,6 +1,6 @@
update-docs.py compile-2.6
update-docs.py compile-2.7
update-docs.py compile-3.5
update-docs.py future-import-boilerplate
update-docs.py metaclass-boilerplate
update-docs.py shebang
tests/update-docs.py compile-2.6
tests/update-docs.py compile-2.7
tests/update-docs.py compile-3.5
tests/update-docs.py future-import-boilerplate
tests/update-docs.py metaclass-boilerplate
tests/update-docs.py shebang

View file

@ -1 +1 @@
update-docs.py shebang
tests/update-docs.py shebang

View file

@ -1 +1 @@
update-docs.py shebang
tests/update-docs.py shebang

View file

@ -1 +1 @@
update-docs.py shebang
tests/update-docs.py shebang

View file

@ -1 +1 @@
update-docs.py shebang
tests/update-docs.py shebang

View file

@ -1 +1 @@
update-docs.py shebang
tests/update-docs.py shebang

View file

@ -1,2 +1 @@
update-docs.py shebang
tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2
tests/update-docs.py shebang

View file

@ -1,2 +1 @@
update-docs.py shebang
tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2
tests/update-docs.py shebang

View file

@ -1,2 +1 @@
update-docs.py shebang
tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2
tests/update-docs.py shebang

View file

@ -0,0 +1 @@
tests/update-docs.py shebang

View file

@ -1,9 +1,9 @@
docs/docsite/rst/api-guide.rst rstcheck
docs/docsite/rst/quoting.rst rstcheck
docs/docsite/rst/ssh-guide.rst rstcheck
update-docs.py compile-2.6
update-docs.py compile-2.7
update-docs.py compile-3.5
update-docs.py future-import-boilerplate
update-docs.py metaclass-boilerplate
update-docs.py shebang
tests/update-docs.py compile-2.6
tests/update-docs.py compile-2.7
tests/update-docs.py compile-3.5
tests/update-docs.py future-import-boilerplate
tests/update-docs.py metaclass-boilerplate
tests/update-docs.py shebang

View file

@ -1,20 +0,0 @@
# Copyright (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
#
# Compat for python2.7
#
# One unittest needs to import builtins via __import__() so we need to have
# the string that represents it
try:
import __builtin__ # noqa: F401, pylint: disable=unused-import
except ImportError:
BUILTINS = 'builtins'
else:
BUILTINS = '__builtin__'

View file

@ -1,109 +0,0 @@
# Copyright (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
'''
Compat module for Python3.x's unittest.mock module
'''
import sys
# Python 2.7
# Note: Could use the pypi mock library on python3.x as well as python2.x. It
# is the same as the python3 stdlib mock library
try:
# Allow wildcard import because we really do want to import all of mock's
# symbols into this compat shim
# pylint: disable=wildcard-import,unused-wildcard-import
from unittest.mock import * # noqa: F401, pylint: disable=unused-import
except ImportError:
# Python 2
# pylint: disable=wildcard-import,unused-wildcard-import
try:
from mock import * # noqa: F401, pylint: disable=unused-import
except ImportError:
print('You need the mock library installed on python2.x to run tests')
# Prior to 3.4.4, mock_open cannot handle binary read_data
if sys.version_info >= (3,) and sys.version_info < (3, 4, 4):
file_spec = None
def _iterate_read_data(read_data):
# Helper for mock_open:
# Retrieve lines from read_data via a generator so that separate calls to
# readline, read, and readlines are properly interleaved
sep = b'\n' if isinstance(read_data, bytes) else '\n'
data_as_list = [l + sep for l in read_data.split(sep)]
if data_as_list[-1] == sep:
# If the last line ended in a newline, the list comprehension will have an
# extra entry that's just a newline. Remove this.
data_as_list = data_as_list[:-1]
else:
# If there wasn't an extra newline by itself, then the file being
# emulated doesn't have a newline to end the last line remove the
# newline that our naive format() added
data_as_list[-1] = data_as_list[-1][:-1]
for line in data_as_list:
yield line
def mock_open(mock=None, read_data=''):
"""
A helper function to create a mock to replace the use of `open`. It works
for `open` called directly or used as a context manager.
The `mock` argument is the mock object to configure. If `None` (the
default) then a `MagicMock` will be created for you, with the API limited
to methods or attributes available on standard file handles.
`read_data` is a string for the `read` methoddline`, and `readlines` of the
file handle to return. This is an empty string by default.
"""
def _readlines_side_effect(*args, **kwargs):
if handle.readlines.return_value is not None:
return handle.readlines.return_value
return list(_data)
def _read_side_effect(*args, **kwargs):
if handle.read.return_value is not None:
return handle.read.return_value
return type(read_data)().join(_data)
def _readline_side_effect():
if handle.readline.return_value is not None:
while True:
yield handle.readline.return_value
for line in _data:
yield line
global file_spec
if file_spec is None:
import _io
file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
if mock is None:
mock = MagicMock(name='open', spec=open)
handle = MagicMock(spec=file_spec)
handle.__enter__.return_value = handle
_data = _iterate_read_data(read_data)
handle.write.return_value = None
handle.read.return_value = None
handle.readline.return_value = None
handle.readlines.return_value = None
handle.read.side_effect = _read_side_effect
handle.readline.side_effect = _readline_side_effect()
handle.readlines.side_effect = _readlines_side_effect
mock.return_value = handle
return mock

View file

@ -1,25 +0,0 @@
# Copyright (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
'''
Compat module for Python2.7's unittest module
'''
import sys
# Allow wildcard import because we really do want to import all of
# unittests's symbols into this compat shim
# pylint: disable=wildcard-import,unused-wildcard-import
if sys.version_info < (2, 7):
try:
# Need unittest2 on python2.6
from unittest2 import * # noqa: F401, pylint: disable=unused-import
except ImportError:
print('You need unittest2 installed on python2.6.x to run tests')
else:
from unittest import * # noqa: F401, pylint: disable=unused-import

View file

@ -9,7 +9,7 @@ __metaclass__ = type
import os
import json
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')

View file

@ -6,9 +6,10 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import FakeLibRouterosError, Key, Or, fake_ros_api
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.plugins.modules import api
@ -34,8 +35,8 @@ class TestRouterosApiModule(ModuleTestCase):
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson) as exc:
set_module_args({})
self.module.main()
with set_module_args({}):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -43,8 +44,8 @@ class TestRouterosApiModule(ModuleTestCase):
@patch('ansible_collections.community.routeros.plugins.modules.api.ROS_api_module.api_add_path', new=fake_ros_api.path)
def test_api_path(self):
with self.assertRaises(AnsibleExitJson) as exc:
set_module_args(self.config_module_args.copy())
self.module.main()
with set_module_args(self.config_module_args.copy()):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -54,8 +55,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['add'] = "name=unit_test_brige"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -65,8 +66,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleFailJson) as exc:
module_args = self.config_module_args.copy()
module_args['add'] = "name=unit_test_brige_exist"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -77,8 +78,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['remove'] = "*A1"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -88,8 +89,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleFailJson) as exc:
module_args = self.config_module_args.copy()
module_args['remove'] = "*A2"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -100,8 +101,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['cmd'] = "add name=unit_test_brige_arbitrary"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -111,8 +112,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleFailJson) as exc:
module_args = self.config_module_args.copy()
module_args['cmd'] = "add NONE_EXIST=unit_test_brige_arbitrary"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -123,8 +124,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['update'] = ".id=*A1 name=unit_test_brige"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -134,8 +135,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleFailJson) as exc:
module_args = self.config_module_args.copy()
module_args['update'] = ".id=*A2 name=unit_test_brige"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -146,8 +147,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['query'] = ".id name"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -162,8 +163,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['query'] = ".id other"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -174,8 +175,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['query'] = ".id name WHERE name == dummy_bridge_A2"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -188,8 +189,8 @@ class TestRouterosApiModule(ModuleTestCase):
with self.assertRaises(AnsibleExitJson) as exc:
module_args = self.config_module_args.copy()
module_args['query'] = ".id name WHERE name != dummy_bridge_A2"
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -204,8 +205,8 @@ class TestRouterosApiModule(ModuleTestCase):
module_args['extended_query'] = {
'attributes': ['.id', 'name'],
}
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -222,8 +223,8 @@ class TestRouterosApiModule(ModuleTestCase):
module_args['extended_query'] = {
'attributes': ['.id', 'other'],
}
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -243,8 +244,8 @@ class TestRouterosApiModule(ModuleTestCase):
},
],
}
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -266,8 +267,8 @@ class TestRouterosApiModule(ModuleTestCase):
},
],
}
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -298,8 +299,8 @@ class TestRouterosApiModule(ModuleTestCase):
},
],
}
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)

View file

@ -6,9 +6,10 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import FakeLibRouterosError, Key, fake_ros_api
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.plugins.modules import api_facts
@ -437,8 +438,8 @@ class TestRouterosApiFactsModule(ModuleTestCase):
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson) as exc:
set_module_args({})
self.module.main()
with set_module_args({}):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -447,8 +448,8 @@ class TestRouterosApiFactsModule(ModuleTestCase):
with self.assertRaises(AnsibleFailJson) as exc:
module_args = self.config_module_args.copy()
module_args['gather_subset'] = ['!foobar']
set_module_args(module_args)
self.module.main()
with set_module_args(module_args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -456,8 +457,8 @@ class TestRouterosApiFactsModule(ModuleTestCase):
def test_full_run(self):
with self.assertRaises(AnsibleExitJson) as exc:
set_module_args(self.config_module_args.copy())
self.module.main()
with set_module_args(self.config_module_args.copy()):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)

View file

@ -6,11 +6,12 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import (
FakeLibRouterosError, fake_ros_api, massage_expected_result_data, create_fake_path,
)
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.plugins.modules import api_find_and_modify
@ -93,6 +94,52 @@ START_IP_FIREWALL_FILTER = [
START_IP_FIREWALL_FILTER_OLD_DATA = massage_expected_result_data(START_IP_FIREWALL_FILTER, ('ip', 'firewall', 'filter'), keep_all=True)
START_IP_SERVICE = [
# I removed all entryes not for 'api' and 'api-ssl'
{
"certificate": None,
"tls-version": None,
".id": "*7",
"address": "",
"disabled": True,
"dynamic": False,
"invalid": True,
"name": "api",
"port": 8728,
"proto": "tcp",
"vrf": "main"
},
{
".id": "*9",
"address": "192.168.1.0/24",
"certificate": "mycert",
"dynamic": False,
"invalid": False,
"name": "api-ssl",
"port": 8729,
"proto": "tcp",
"tls-version": "only-1.2",
"vrf": "main"
},
{
"address": None,
"certificate": None,
"max-sessions": None,
"tls-version": None,
".id": "*13",
"connection": True,
"dynamic": True,
"invalid": False,
"local": "192.168.1.1",
"name": "api-ssl",
"port": 8729,
"proto": "tcp",
"remote": "192.168.1.2:12346"
}
]
START_IP_SERVICE_OLD_DATA = massage_expected_result_data(START_IP_SERVICE, ('ip', 'service'), keep_all=True)
class TestRouterosApiFindAndModifyModule(ModuleTestCase):
@ -117,8 +164,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson) as exc:
set_module_args({})
self.module.main()
with set_module_args({}):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -136,8 +183,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'bar',
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -155,8 +202,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'bar',
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -173,8 +220,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'!comment': None,
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -190,8 +237,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'!comment': 'gone',
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -212,8 +259,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
},
'require_matches_min': 10,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -234,8 +281,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
},
'require_matches_min': 10,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -256,8 +303,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
},
'require_matches_max': 1,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -277,8 +324,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'name': 'bam',
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -303,8 +350,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'require_matches_min': 2,
'allow_no_matches': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -325,8 +372,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'values': {
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -349,8 +396,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': None,
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -374,8 +421,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
},
'_ansible_diff': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -450,8 +497,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': None,
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -502,8 +549,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': '',
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -553,8 +600,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'!comment': None,
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -606,8 +653,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'!connection-state': None,
},
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -620,6 +667,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'defconf',
'protocol': 'icmp',
'disabled': False,
'log': False,
'log-prefix': '',
},
{
'.id': '*3',
@ -627,6 +676,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'chain': 'input',
'comment': 'defconf',
'disabled': False,
'log': False,
'log-prefix': '',
},
{
'.id': '*4',
@ -634,6 +685,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'chain': 'input',
'comment': 'defconf',
'disabled': False,
'log': False,
'log-prefix': '',
},
{
'.id': '*7',
@ -642,6 +695,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'defconf',
'disabled': False,
'in-interface': 'wan',
'log': False,
'log-prefix': '',
},
{
'.id': '*8',
@ -650,6 +705,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'defconf',
'connection-state': 'established',
'disabled': False,
'log': False,
'log-prefix': '',
},
{
'.id': '*9',
@ -658,6 +715,8 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'defconf',
'connection-state': 'related',
'disabled': False,
'log': False,
'log-prefix': '',
},
{
'.id': '*A',
@ -666,7 +725,35 @@ class TestRouterosApiFindAndModifyModule(ModuleTestCase):
'comment': 'defconf',
'connection-status': 'invalid',
'disabled': False,
'log': False,
'log-prefix': '',
},
])
self.assertEqual(result['match_count'], 3)
self.assertEqual(result['modify_count'], 2)
@patch('ansible_collections.community.routeros.plugins.modules.api_find_and_modify.compose_api_path',
new=create_fake_path(('ip', 'service'), START_IP_SERVICE))
def test_change_ignore_dynamic(self):
with self.assertRaises(AnsibleExitJson) as exc:
args = self.config_module_args.copy()
args.update({
'path': 'ip service',
'find': {
'name': 'api-ssl',
},
'values': {
'address': '192.168.1.0/24',
},
'ignore_dynamic': True,
'_ansible_diff': True,
})
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
self.assertEqual(result['old_data'], [entry for entry in START_IP_SERVICE_OLD_DATA if entry["dynamic"] is False])
self.assertEqual(result['new_data'], [entry for entry in START_IP_SERVICE_OLD_DATA if entry["dynamic"] is False])
self.assertEqual(result['match_count'], 1)
self.assertEqual(result['modify_count'], 0)

View file

@ -6,11 +6,12 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import (
FAKE_ROS_VERSION, FakeLibRouterosError, Key, fake_ros_api,
)
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.plugins.modules import api_info
@ -41,8 +42,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson) as exc:
set_module_args({})
self.module.main()
with set_module_args({}):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -53,8 +54,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
args.update({
'path': 'something invalid'
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -68,8 +69,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
args.update({
'path': 'ip dns static'
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -93,8 +94,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
args.update({
'path': 'caps-man aaa',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -122,8 +123,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'caps-man aaa',
'hide_defaults': False,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -155,8 +156,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'caps-man aaa',
'unfiltered': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -189,8 +190,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'ip firewall filter',
'handle_disabled': 'exclamation',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -278,8 +279,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'ip firewall filter',
'handle_disabled': 'null-value',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -367,8 +368,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'ip firewall filter',
'handle_disabled': 'omit',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -402,8 +403,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'handle_disabled': 'omit',
'include_dynamic': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -460,8 +461,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'interface list',
'handle_disabled': 'omit',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -513,8 +514,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'handle_disabled': 'omit',
'include_builtin': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -605,8 +606,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'path': 'ip dhcp-server lease',
'handle_disabled': 'omit',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -688,8 +689,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
args.update({
'path': 'interface gre',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -776,8 +777,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'handle_disabled': 'omit',
'hide_defaults': False,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -847,8 +848,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'handle_disabled': 'omit',
'restrict': [],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -899,8 +900,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
'values': ['forward'],
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -970,8 +971,8 @@ class TestRouterosApiInfoModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)

View file

@ -6,11 +6,12 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch, MagicMock
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.tests.unit.plugins.modules.fake_api import (
FAKE_ROS_VERSION, FakeLibRouterosError, fake_ros_api, massage_expected_result_data, create_fake_path,
)
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args, AnsibleExitJson, AnsibleFailJson, ModuleTestCase
from ansible_collections.community.routeros.plugins.modules import api_modify
@ -318,8 +319,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson) as exc:
set_module_args({})
self.module.main()
with set_module_args({}):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -331,8 +332,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'path': 'something invalid',
'data': [],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -348,8 +349,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'foo': 'bar',
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -366,8 +367,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'!comment': None,
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -383,8 +384,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'!disabled': None,
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -400,8 +401,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'!comment': 'foo',
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -416,8 +417,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'name': None,
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -432,8 +433,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'interface': 'eth0',
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -448,8 +449,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'address': '192.168.88.1',
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -466,8 +467,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'address': '192.168.88.1',
}],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['failed'], True)
@ -498,8 +499,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -533,8 +534,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -556,8 +557,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -591,8 +592,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -656,8 +657,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -722,8 +723,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
],
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -785,8 +786,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -844,8 +845,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -903,8 +904,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -963,8 +964,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1017,8 +1018,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1077,8 +1078,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1128,8 +1129,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1164,8 +1165,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1212,8 +1213,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1285,8 +1286,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'ensure_order': True,
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1340,8 +1341,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1363,8 +1364,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
],
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1386,8 +1387,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1426,8 +1427,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
],
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1466,8 +1467,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
],
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1507,8 +1508,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1551,8 +1552,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
},
],
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1587,8 +1588,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1621,8 +1622,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'handle_entries_content': 'remove',
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1676,8 +1677,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1729,8 +1730,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1785,8 +1786,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'ensure_order': True,
'_ansible_check_mode': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1847,8 +1848,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1879,8 +1880,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -1911,8 +1912,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_entries_content': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], True)
@ -1969,8 +1970,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)
@ -2005,8 +2006,8 @@ class TestRouterosApiModifyModule(ModuleTestCase):
'handle_absent_entries': 'remove',
'ensure_order': True,
})
set_module_args(args)
self.module.main()
with set_module_args(args):
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], False)

View file

@ -8,9 +8,10 @@ __metaclass__ = type
import json
from ansible_collections.community.routeros.tests.unit.compat.mock import patch
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args
from ansible_collections.community.routeros.plugins.modules import command
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args
from .routeros_module import TestRouterosModule, load_fixture
@ -47,54 +48,54 @@ class TestRouterosCommandModule(TestRouterosModule):
self.run_commands.side_effect = load_from_file
def test_command_simple(self):
set_module_args(dict(commands=['/system resource print']))
result = self.execute_module(changed=True)
with set_module_args(dict(commands=['/system resource print'])):
result = self.execute_module(changed=True)
self.assertEqual(len(result['stdout']), 1)
self.assertTrue('platform: "MikroTik"' in result['stdout'][0])
def test_command_multiple(self):
set_module_args(dict(commands=['/system resource print', '/system resource print']))
result = self.execute_module(changed=True)
with set_module_args(dict(commands=['/system resource print', '/system resource print'])):
result = self.execute_module(changed=True)
self.assertEqual(len(result['stdout']), 2)
self.assertTrue('platform: "MikroTik"' in result['stdout'][0])
def test_command_wait_for(self):
wait_for = 'result[0] contains "MikroTik"'
set_module_args(dict(commands=['/system resource print'], wait_for=wait_for))
self.execute_module(changed=True)
with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)):
self.execute_module(changed=True)
def test_command_wait_for_fails(self):
wait_for = 'result[0] contains "test string"'
set_module_args(dict(commands=['/system resource print'], wait_for=wait_for))
self.execute_module(failed=True)
with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for)):
self.execute_module(failed=True)
self.assertEqual(self.run_commands.call_count, 10)
def test_command_retries(self):
wait_for = 'result[0] contains "test string"'
set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, retries=2))
self.execute_module(failed=True)
with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, retries=2)):
self.execute_module(failed=True)
self.assertEqual(self.run_commands.call_count, 2)
def test_command_match_any(self):
wait_for = ['result[0] contains "MikroTik"',
'result[0] contains "test string"']
set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='any'))
self.execute_module(changed=True)
with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='any')):
self.execute_module(changed=True)
def test_command_match_all(self):
wait_for = ['result[0] contains "MikroTik"',
'result[0] contains "RB1100"']
set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='all'))
self.execute_module(changed=True)
with set_module_args(dict(commands=['/system resource print'], wait_for=wait_for, match='all')):
self.execute_module(changed=True)
def test_command_match_all_failure(self):
wait_for = ['result[0] contains "MikroTik"',
'result[0] contains "test string"']
commands = ['/system resource print', '/system resource print']
set_module_args(dict(commands=commands, wait_for=wait_for, match='all'))
self.execute_module(failed=True)
with set_module_args(dict(commands=commands, wait_for=wait_for, match='all')):
self.execute_module(failed=True)
def test_command_wait_for_2(self):
wait_for = 'result[0] contains "wireless"'
set_module_args(dict(commands=['/system package print'], wait_for=wait_for))
self.execute_module(changed=True)
with set_module_args(dict(commands=['/system package print'], wait_for=wait_for)):
self.execute_module(changed=True)

View file

@ -6,9 +6,10 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible_collections.community.routeros.tests.unit.compat.mock import patch
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import set_module_args
from ansible_collections.community.routeros.plugins.modules import facts
from ansible_collections.community.routeros.tests.unit.plugins.modules.utils import set_module_args
from .routeros_module import TestRouterosModule, load_fixture
@ -39,8 +40,8 @@ class TestRouterosFactsModule(TestRouterosModule):
self.run_commands.side_effect = load_from_file
def test_facts_default(self):
set_module_args(dict(gather_subset='default'))
result = self.execute_module()
with set_module_args(dict(gather_subset='default')):
result = self.execute_module()
self.assertEqual(
result['ansible_facts']['ansible_net_hostname'], 'MikroTik'
)
@ -61,8 +62,8 @@ class TestRouterosFactsModule(TestRouterosModule):
)
def test_facts_hardware(self):
set_module_args(dict(gather_subset='hardware'))
result = self.execute_module()
with set_module_args(dict(gather_subset='hardware')):
result = self.execute_module()
self.assertEqual(
result['ansible_facts']['ansible_net_spacefree_mb'], 64921.6
)
@ -77,8 +78,8 @@ class TestRouterosFactsModule(TestRouterosModule):
)
def test_facts_config(self):
set_module_args(dict(gather_subset='config'))
result = self.execute_module()
with set_module_args(dict(gather_subset='config')):
result = self.execute_module()
self.assertIsInstance(
result['ansible_facts']['ansible_net_config'], str
)
@ -88,8 +89,8 @@ class TestRouterosFactsModule(TestRouterosModule):
)
def test_facts_interfaces(self):
set_module_args(dict(gather_subset='interfaces'))
result = self.execute_module()
with set_module_args(dict(gather_subset='interfaces')):
result = self.execute_module()
self.assertIn(
result['ansible_facts']['ansible_net_all_ipv4_addresses'][0], ['10.37.129.3', '10.37.0.0', '192.168.88.1']
)
@ -118,8 +119,8 @@ class TestRouterosFactsModule(TestRouterosModule):
self.assertEqual(result, None)
def test_facts_routing(self):
set_module_args(dict(gather_subset='routing'))
result = self.execute_module()
with set_module_args(dict(gather_subset='routing')):
result = self.execute_module()
self.assertIn(
result['ansible_facts']['ansible_net_bgp_peer']['iBGP_BRAS.TYRMA']['name'], ['iBGP_BRAS.TYRMA']
)

View file

@ -1,54 +0,0 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
from ansible_collections.community.routeros.tests.unit.compat import unittest
from ansible_collections.community.routeros.tests.unit.compat.mock import patch
from ansible.module_utils import basic
from ansible.module_utils.common.text.converters import to_bytes
def set_module_args(args):
if '_ansible_remote_tmp' not in args:
args['_ansible_remote_tmp'] = '/tmp'
if '_ansible_keep_remote_files' not in args:
args['_ansible_keep_remote_files'] = False
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
basic._ANSIBLE_ARGS = to_bytes(args)
class AnsibleExitJson(Exception):
pass
class AnsibleFailJson(Exception):
pass
def exit_json(*args, **kwargs):
if 'changed' not in kwargs:
kwargs['changed'] = False
raise AnsibleExitJson(kwargs)
def fail_json(*args, **kwargs):
kwargs['failed'] = True
raise AnsibleFailJson(kwargs)
class ModuleTestCase(unittest.TestCase):
def setUp(self):
self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json)
self.mock_module.start()
self.mock_sleep = patch('time.sleep')
self.mock_sleep.start()
set_module_args({})
self.addCleanup(self.mock_module.stop)
self.addCleanup(self.mock_sleep.stop)

View file

@ -4,4 +4,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
collections:
- ansible.netcommon
- community.internal_test_tools

29
update-docs.py → tests/update-docs.py Executable file → Normal file
View file

@ -11,9 +11,6 @@ Updates DOCUMENTATION of modules using module_utils._api_data with the correct l
import sys
# Ensure that we can import things from ansible_collections
sys.path.append('../../..')
from ansible_collections.community.routeros.plugins.module_utils._api_data import (
PATHS,
join_path,
@ -26,24 +23,34 @@ MODULES = [
]
def update_file(file, begin_line, end_line, choice_line, path_choices):
def update_file(file: str, begin_line: str, end_line: str, choice_line: str, path_choices: list[str]) -> bool:
with open(file, 'r', encoding='utf-8') as f:
lines = f.read().splitlines()
begin_index = lines.index(begin_line)
end_index = lines.index(end_line, begin_index + 1)
new_lines = lines[:begin_index + 1] + [choice_line.format(choice=choice) for choice in path_choices] + lines[end_index:]
if lines != new_lines:
print(f'{file} has been updated')
with open(file, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines) + '\n')
if lines == new_lines:
return False
print(f'{file} has been updated')
with open(file, 'w', encoding='utf-8') as f:
f.write('\n'.join(new_lines) + '\n')
return True
def main():
def main(args: list[str]) -> int:
path_choices = sorted([join_path(path) for path, path_info in PATHS.items() if path_info.fully_understood])
changes = False
for file in MODULES:
update_file(file, ' # BEGIN PATH LIST', ' # END PATH LIST', ' - {choice}', path_choices)
changes |= update_file(file, ' # BEGIN PATH LIST', ' # END PATH LIST', ' - {choice}', path_choices)
lint = "--lint" in args
if not lint or not changes:
return 0
print("Run 'nox -Re update-docs'!")
return 1
if __name__ == '__main__':
main()
sys.exit(main(sys.argv[1:]))