mirror of
https://github.com/ansible-collections/ansible.posix.git
synced 2026-03-26 21:33:32 +00:00
Compare commits
609 Commits
0.1.2
...
516071efd5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
516071efd5 | ||
|
|
b96fad5e5b | ||
|
|
2d350e6073 | ||
|
|
ea6ef5c775 | ||
|
|
9007e20b2f | ||
|
|
508e3d920f | ||
|
|
18e023dd77 | ||
|
|
d3204df536 | ||
|
|
162e72182f | ||
|
|
1c1dd005c8 | ||
|
|
40c27e2bee | ||
|
|
c273ac2a01 | ||
|
|
f978998521 | ||
|
|
c319c856ed | ||
|
|
cdb442ea48 | ||
|
|
f977bffff2 | ||
|
|
dabaca4b70 | ||
|
|
26b9b1438d | ||
|
|
1994b2cf1c | ||
|
|
2f224e6a6a | ||
|
|
96ec2097cc | ||
|
|
7b9b1f4957 | ||
|
|
f0b5f039d3 | ||
|
|
5ceb40b600 | ||
|
|
2cec8cbed5 | ||
|
|
1b8aeb03cb | ||
|
|
eead50b287 | ||
|
|
4ff0e3aa13 | ||
|
|
979f00ce75 | ||
|
|
ed3d322fd5 | ||
|
|
d9f54eb9d4 | ||
|
|
7d8da00f79 | ||
|
|
f087d58cbe | ||
|
|
eb740e97d4 | ||
|
|
44a2151dbf | ||
|
|
83288b9020 | ||
|
|
4c6898ee6e | ||
|
|
f5eae12146 | ||
|
|
f632fad9d4 | ||
|
|
74edb72249 | ||
|
|
91129ed381 | ||
|
|
7b8d627552 | ||
|
|
6175a5028b | ||
|
|
8e34860d6c | ||
|
|
1b9ae1c77f | ||
|
|
ae95adcfdc | ||
|
|
c654bf0b4c | ||
|
|
04283b5993 | ||
|
|
b0097bd14c | ||
|
|
b991f4f9af | ||
|
|
6280bb8ec8 | ||
|
|
3b79155e68 | ||
|
|
5eacaba86d | ||
|
|
8b611775d6 | ||
|
|
9171b7fcf1 | ||
|
|
05724a097b | ||
|
|
2f095566c7 | ||
|
|
daca208c0b | ||
|
|
4d928119de | ||
|
|
906bbb88b7 | ||
|
|
57be05289c | ||
|
|
9df5aac2a5 | ||
|
|
098b5bee70 | ||
|
|
3a085b0b10 | ||
|
|
cd43bd10bb | ||
|
|
9cf2d8cc48 | ||
|
|
2041e7f918 | ||
|
|
f4baa4c6d8 | ||
|
|
afa724ba8a | ||
|
|
0821768bcb | ||
|
|
5f3f8514eb | ||
|
|
5321a9ecb5 | ||
|
|
7194b6bb13 | ||
|
|
a85108e25e | ||
|
|
c353e43a90 | ||
|
|
7e4d5dd7a9 | ||
|
|
e05b8507a4 | ||
|
|
4f0114eb57 | ||
|
|
6ab2053005 | ||
|
|
e26d9664ba | ||
|
|
97c022871b | ||
|
|
d968ac441e | ||
|
|
e00a4299c1 | ||
|
|
0ed72d0004 | ||
|
|
4efdb43ccd | ||
|
|
52d140895b | ||
|
|
257392f33d | ||
|
|
0df6c21af4 | ||
|
|
71b48d3b5f | ||
|
|
9a2ddcd713 | ||
|
|
f2d0b38b0e | ||
|
|
7d219a7e05 | ||
|
|
f41f08e9e3 | ||
|
|
41fc0901d9 | ||
|
|
738a839ce5 | ||
|
|
c07ebc2f9c | ||
|
|
7bf56953cb | ||
|
|
7b9eb8d56e | ||
|
|
c29e8c213b | ||
|
|
39a9e79985 | ||
|
|
d80fde7ea0 | ||
|
|
271119d196 | ||
|
|
af6b90981a | ||
|
|
0ea18d208f | ||
|
|
d2f1cc063a | ||
|
|
55fb80cf5e | ||
|
|
3bff8d22a5 | ||
|
|
7313065a96 | ||
|
|
38312cb8e1 | ||
|
|
ef07eb8a06 | ||
|
|
0c9ab06a55 | ||
|
|
b228507b62 | ||
|
|
0341c9420f | ||
|
|
b976831cef | ||
|
|
fd78e3e6da | ||
|
|
11f29eba6f | ||
|
|
a615b84bf7 | ||
|
|
9ccc24edf2 | ||
|
|
7e1b76c46e | ||
|
|
505a4aaa09 | ||
|
|
d70d2aaaa7 | ||
|
|
806ff5c1a3 | ||
|
|
34f140c22f | ||
|
|
83c4d2abd1 | ||
|
|
e5733c5e49 | ||
|
|
d49bd27fae | ||
|
|
9064ff7eb2 | ||
|
|
a842e5f96a | ||
|
|
97dcdee670 | ||
|
|
55ea4ba1de | ||
|
|
a88f5f8ae0 | ||
|
|
d36a289deb | ||
|
|
60c8d23c7f | ||
|
|
795d8e34e7 | ||
|
|
b615d61fae | ||
|
|
bda85b7453 | ||
|
|
ac82f575c6 | ||
|
|
a356bdd3c4 | ||
|
|
4f93234a8b | ||
|
|
9bfed58f7f | ||
|
|
a18d180246 | ||
|
|
51b94f536c | ||
|
|
0a07bdb358 | ||
|
|
e929aad539 | ||
|
|
0847977d12 | ||
|
|
2a1fb334ee | ||
|
|
af870d0b83 | ||
|
|
8e900e5218 | ||
|
|
45d8819b7c | ||
|
|
6473053b13 | ||
|
|
046f6d1483 | ||
|
|
2c52f969e1 | ||
|
|
011c60e736 | ||
|
|
a85f736f6a | ||
|
|
05ee6ebc2a | ||
|
|
c394f6778d | ||
|
|
748bad22ad | ||
|
|
6f95c8b356 | ||
|
|
695fa213b3 | ||
|
|
719f7dfebf | ||
|
|
5cae7aa946 | ||
|
|
2f699307c7 | ||
|
|
7dd5087e7b | ||
|
|
542643e786 | ||
|
|
2cde4cdb26 | ||
|
|
12a1c25f5e | ||
|
|
7062be892e | ||
|
|
20c2c30d23 | ||
|
|
4f1e6b4a6d | ||
|
|
806ab9ef9d | ||
|
|
2ed8bdc8e8 | ||
|
|
622aef2aad | ||
|
|
5165572e9e | ||
|
|
6c46a924b9 | ||
|
|
ab1bc5df9f | ||
|
|
4106ec65f9 | ||
|
|
85c958ccb8 | ||
|
|
ad414c87b4 | ||
|
|
34a9cf3e4d | ||
|
|
63fba50912 | ||
|
|
8ec2c261bd | ||
|
|
f48515aad3 | ||
|
|
3bbf2e3cd5 | ||
|
|
ae0380d671 | ||
|
|
4f46ff218f | ||
|
|
07f1e11015 | ||
|
|
202609e425 | ||
|
|
f1b36ba1c2 | ||
|
|
0eb6a7a437 | ||
|
|
15954f8def | ||
|
|
41ce2cc8f9 | ||
|
|
b9feff586f | ||
|
|
281f957ece | ||
|
|
da1713ed3e | ||
|
|
7fb9cc95c3 | ||
|
|
3a5ba91d92 | ||
|
|
1c560e93d2 | ||
|
|
daf0b89fcb | ||
|
|
083f3aab64 | ||
|
|
9d1f67042e | ||
|
|
cddfa80d84 | ||
|
|
91a6e30d78 | ||
|
|
940d914904 | ||
|
|
b6587a783e | ||
|
|
7d069b597f | ||
|
|
38dd009615 | ||
|
|
7cf3934ef0 | ||
|
|
26c182c8ef | ||
|
|
ce24b6c746 | ||
|
|
a417ac80f0 | ||
|
|
009ed3bed5 | ||
|
|
1eb594b836 | ||
|
|
f47c5d6039 | ||
|
|
ab94cc7294 | ||
|
|
0e92d30fd3 | ||
|
|
583e4a6d6b | ||
|
|
dc7ee92827 | ||
|
|
a62acdfc5a | ||
|
|
01f19cde25 | ||
|
|
32742c3486 | ||
|
|
553b49245f | ||
|
|
84c56e1814 | ||
|
|
41d8029ef5 | ||
|
|
5eb019b2c1 | ||
|
|
94523978d2 | ||
|
|
14accca52f | ||
|
|
d64db13643 | ||
|
|
fd32da0e99 | ||
|
|
0d2ff1d2d8 | ||
|
|
2ac6fbb84b | ||
|
|
b2f053a856 | ||
|
|
a9920ae189 | ||
|
|
ee9df94762 | ||
|
|
8a07431bf8 | ||
|
|
02de0c6a76 | ||
|
|
42af89d019 | ||
|
|
8ccd0b800b | ||
|
|
727efe9fd2 | ||
|
|
bbc511dbc8 | ||
|
|
6695394af6 | ||
|
|
04b8a5f918 | ||
|
|
f109c162b0 | ||
|
|
a2ab6881db | ||
|
|
ec72fdc3ab | ||
|
|
d6a997b37d | ||
|
|
d537e7ded8 | ||
|
|
bd9aa64a2b | ||
|
|
6b7dc6ee0a | ||
|
|
a831f22b83 | ||
|
|
cf8e77cf7d | ||
|
|
c4742cfa81 | ||
|
|
cc93ee8232 | ||
|
|
e97087e616 | ||
|
|
e647e147a1 | ||
|
|
9575b9be64 | ||
|
|
4229db1bbe | ||
|
|
bf0ad4aad2 | ||
|
|
0fff8fde30 | ||
|
|
adcb28f806 | ||
|
|
d0e1504f8a | ||
|
|
090706b581 | ||
|
|
e52ae8a9bc | ||
|
|
7df358d74f | ||
|
|
dcd9598e48 | ||
|
|
fc5894171d | ||
|
|
a3b8fdbf25 | ||
|
|
69228e79d2 | ||
|
|
d1fbbb7905 | ||
|
|
50f87b0d15 | ||
|
|
4512e7b1e9 | ||
|
|
297a10fec7 | ||
|
|
139e103b0f | ||
|
|
83a933772c | ||
|
|
6c9616291e | ||
|
|
0224e4d415 | ||
|
|
cc20deaad2 | ||
|
|
5e0a6592ca | ||
|
|
acd5a2b17e | ||
|
|
e629ae2d7c | ||
|
|
abfe36c62f | ||
|
|
6c0b91a3ee | ||
|
|
f7fac90009 | ||
|
|
c4be75114b | ||
|
|
6da0cbb3d3 | ||
|
|
17fc3bcce6 | ||
|
|
d7c7d1d2c8 | ||
|
|
b8ed919011 | ||
|
|
04089e80fb | ||
|
|
55bd196e82 | ||
|
|
f451f2211b | ||
|
|
18c847a8a0 | ||
|
|
ee65205607 | ||
|
|
2c9c3a0f54 | ||
|
|
c1f830f3df | ||
|
|
2d3f55caa7 | ||
|
|
ab3bf45c6c | ||
|
|
00a22a525c | ||
|
|
eebfff4eb8 | ||
|
|
2ee9cc533a | ||
|
|
6d5145e285 | ||
|
|
45519c68be | ||
|
|
33f2ef8f11 | ||
|
|
dc4da60aff | ||
|
|
c520b68276 | ||
|
|
3ab812953f | ||
|
|
0e71c0e530 | ||
|
|
04fdbffca1 | ||
|
|
def5d2bae0 | ||
|
|
aecf81a23b | ||
|
|
373250a0ce | ||
|
|
fa4dd35d66 | ||
|
|
211e6c74b5 | ||
|
|
62d060a173 | ||
|
|
b5247a27d4 | ||
|
|
792714f598 | ||
|
|
b886d7691f | ||
|
|
a383c6b27c | ||
|
|
ac611b801f | ||
|
|
68e7ddb7a7 | ||
|
|
cbd915c8ea | ||
|
|
3acced944b | ||
|
|
90cdaf873d | ||
|
|
44a23e363b | ||
|
|
a45448bb26 | ||
|
|
95785bbbbd | ||
|
|
43ead73934 | ||
|
|
16436f4f55 | ||
|
|
cd04f5a092 | ||
|
|
a8fcc028c0 | ||
|
|
667ebef95c | ||
|
|
f17fda3162 | ||
|
|
35a910a698 | ||
|
|
90385b97d8 | ||
|
|
a42d7f0420 | ||
|
|
12c00aa810 | ||
|
|
deac6a412e | ||
|
|
9525e23dc7 | ||
|
|
69a46eb0e8 | ||
|
|
36efb13c23 | ||
|
|
607b7cfcb4 | ||
|
|
af2af9cff3 | ||
|
|
40a9ff36bb | ||
|
|
2768c58f90 | ||
|
|
a15e376bb9 | ||
|
|
e366b90f39 | ||
|
|
d1be5519e6 | ||
|
|
3514f9d3dc | ||
|
|
9890937bcc | ||
|
|
8fda5f8678 | ||
|
|
399f5e2f09 | ||
|
|
1ca54d181d | ||
|
|
8f58cbb41c | ||
|
|
aaa96c2011 | ||
|
|
6ca7a7c0a4 | ||
|
|
4304552677 | ||
|
|
b09750ca7f | ||
|
|
5a2b3662cb | ||
|
|
595ee76b69 | ||
|
|
96c342fd67 | ||
|
|
53d47e1763 | ||
|
|
6e2230c1b3 | ||
|
|
f0f0b0a2e2 | ||
|
|
3d57a17ed6 | ||
|
|
610717ca76 | ||
|
|
dc8f7d68df | ||
|
|
1626c3d5e6 | ||
|
|
e396e5cb43 | ||
|
|
4a67de7b08 | ||
|
|
7f16f56c5b | ||
|
|
391d113a95 | ||
|
|
01bccb08a9 | ||
|
|
249e64a64d | ||
|
|
12941f6d9a | ||
|
|
617302ba94 | ||
|
|
ab9eaf24bc | ||
|
|
fad1d3a870 | ||
|
|
c21fb99f54 | ||
|
|
a65807edc3 | ||
|
|
dad5379461 | ||
|
|
bd594970a8 | ||
|
|
c244abd82f | ||
|
|
42d2b9c65a | ||
|
|
7a62e01af6 | ||
|
|
18614bb82d | ||
|
|
944f5416c9 | ||
|
|
ce3d0fdfa4 | ||
|
|
244c9b2066 | ||
|
|
f85f2904c9 | ||
|
|
db12a40a4c | ||
|
|
5282ece77e | ||
|
|
133ffe51fb | ||
|
|
b3e395a4a3 | ||
|
|
424004c4c0 | ||
|
|
dd2782d81c | ||
|
|
f7f5f89899 | ||
|
|
2b7a35c346 | ||
|
|
fee81b19a8 | ||
|
|
1ebacfb195 | ||
|
|
ab5c1d9911 | ||
|
|
4be66cfb34 | ||
|
|
c5d49b2a91 | ||
|
|
a03e652437 | ||
|
|
e815909859 | ||
|
|
f2601b01da | ||
|
|
27434455e5 | ||
|
|
25cfc56f6f | ||
|
|
f6fa00bfff | ||
|
|
68263bfc29 | ||
|
|
e23ea12cc5 | ||
|
|
69d17ed4b7 | ||
|
|
ed573a8223 | ||
|
|
dd7d29495d | ||
|
|
c596feab78 | ||
|
|
606e3cfe07 | ||
|
|
23fadc9f02 | ||
|
|
6d51660be4 | ||
|
|
957a7420ca | ||
|
|
b740bdaf3a | ||
|
|
049292669b | ||
|
|
310c68e6dd | ||
|
|
fcb2a466e0 | ||
|
|
757b544cf3 | ||
|
|
9f384c00a3 | ||
|
|
0118bf0cb9 | ||
|
|
a994a58b23 | ||
|
|
23afcce8b8 | ||
|
|
e2a316122a | ||
|
|
1671173e99 | ||
|
|
0b597f2e66 | ||
|
|
e068522325 | ||
|
|
51d117285f | ||
|
|
c29bbd265b | ||
|
|
7417d857f1 | ||
|
|
ceddb849b8 | ||
|
|
f5666924ab | ||
|
|
7bed8ce79e | ||
|
|
7d928e6e9d | ||
|
|
07fe3a91b6 | ||
|
|
5cebc90623 | ||
|
|
6ba8445fb1 | ||
|
|
9d4ae8b7e4 | ||
|
|
41e5b8428f | ||
|
|
1793cd70c4 | ||
|
|
b98c7926da | ||
|
|
5d0c5ae3a6 | ||
|
|
cfff8a3806 | ||
|
|
7e6adc977f | ||
|
|
54ea8777f8 | ||
|
|
5d0c8e40d8 | ||
|
|
eaf3d666cc | ||
|
|
c985096af5 | ||
|
|
92c6037900 | ||
|
|
0bef7bb164 | ||
|
|
6e60b0d454 | ||
|
|
0e6f8ab976 | ||
|
|
6b41ce1a9d | ||
|
|
052a2f6d94 | ||
|
|
13e77fd7da | ||
|
|
1f15216d0d | ||
|
|
9173166223 | ||
|
|
da82df4004 | ||
|
|
9313bf3e68 | ||
|
|
8ae6651cff | ||
|
|
86b32d8730 | ||
|
|
707523a763 | ||
|
|
8b1b4df54f | ||
|
|
fc9fce718d | ||
|
|
7511421440 | ||
|
|
553b0ea4f7 | ||
|
|
20e294e026 | ||
|
|
18469dbb3e | ||
|
|
d976add5da | ||
|
|
ecd5ad53e0 | ||
|
|
f6f436f227 | ||
|
|
355a99f779 | ||
|
|
c8b58875d9 | ||
|
|
bd8a3f35c2 | ||
|
|
edec4d767d | ||
|
|
0a56fb0e46 | ||
|
|
f3b6b5e690 | ||
|
|
d65a36e9ea | ||
|
|
252b531c20 | ||
|
|
bc88258687 | ||
|
|
1747370f30 | ||
|
|
72353d3f04 | ||
|
|
1822789d95 | ||
|
|
ccbb679fc3 | ||
|
|
98c86c54cd | ||
|
|
354239d6c9 | ||
|
|
4825036c7e | ||
|
|
360d0c3441 | ||
|
|
d8fb68514c | ||
|
|
d8c6add988 | ||
|
|
ea8fc70373 | ||
|
|
20f70caa1f | ||
|
|
04f976d7d3 | ||
|
|
b6a2dee8bb | ||
|
|
5b66052067 | ||
|
|
2655a6ffc1 | ||
|
|
bbba9beb70 | ||
|
|
a8594c5477 | ||
|
|
3043f737a0 | ||
|
|
080ae28e7d | ||
|
|
e1dad76ccb | ||
|
|
bc0fb3096b | ||
|
|
1e7d82af6d | ||
|
|
41d49e4e08 | ||
|
|
6241e09ccd | ||
|
|
679fe78f32 | ||
|
|
b8c5d6b1b7 | ||
|
|
665f84d996 | ||
|
|
c359200e80 | ||
|
|
793b039691 | ||
|
|
ef36fdc17d | ||
|
|
bcb08106d9 | ||
|
|
cbf54f214c | ||
|
|
931326fb70 | ||
|
|
6343dbdcff | ||
|
|
34a12eb3f9 | ||
|
|
402e0b1f76 | ||
|
|
1268d4aa0a | ||
|
|
5988748999 | ||
|
|
5935dce47f | ||
|
|
77df4ba3fd | ||
|
|
1e84bce91a | ||
|
|
d1fff45191 | ||
|
|
0eeaf61a1a | ||
|
|
c96be65ec9 | ||
|
|
4b0b50439d | ||
|
|
0177b87cbc | ||
|
|
079ba357d4 | ||
|
|
e5e36d3c7b | ||
|
|
f68cec59e7 | ||
|
|
402ec20076 | ||
|
|
2dfa6e28a1 | ||
|
|
c390183337 | ||
|
|
06efaeb108 | ||
|
|
12e0c72841 | ||
|
|
8d568fb6ea | ||
|
|
210ed7c147 | ||
|
|
dd249846ec | ||
|
|
53bb7312e4 | ||
|
|
32350028b6 | ||
|
|
85b43f9059 | ||
|
|
d944df3bde | ||
|
|
be61219931 | ||
|
|
4aba0d1e93 | ||
|
|
3df097259c | ||
|
|
51a1e6d59a | ||
|
|
ff154e5d3b | ||
|
|
04441336fc | ||
|
|
8c9bb90629 | ||
|
|
116b38a946 | ||
|
|
b32447d107 | ||
|
|
78623fd7de | ||
|
|
6f822d08d4 | ||
|
|
5dee4b0576 | ||
|
|
e82c3907bb | ||
|
|
ea952f0825 | ||
|
|
4f2c75a775 | ||
|
|
54f74612e6 | ||
|
|
0338fc5a8f | ||
|
|
f7fc4a2504 | ||
|
|
cd13b2edbc | ||
|
|
7912ef56b8 | ||
|
|
ffd90de595 | ||
|
|
ab01d216fc | ||
|
|
d9827556a4 | ||
|
|
711e5fefdd | ||
|
|
829c0ce51c | ||
|
|
20c113f4c3 | ||
|
|
492d5e3d05 | ||
|
|
e02e9d17d2 | ||
|
|
916b96b4d9 | ||
|
|
e1e748fb04 | ||
|
|
a608931b9e | ||
|
|
89118af1f3 | ||
|
|
581349b700 | ||
|
|
d816868d49 | ||
|
|
f7ac380b82 | ||
|
|
11bd762953 | ||
|
|
82daf8ba9f | ||
|
|
86a5950efa | ||
|
|
01d06f6be3 | ||
|
|
0d0f8217cf | ||
|
|
691d4c77da | ||
|
|
39c09e778c | ||
|
|
9cd270461f | ||
|
|
00f97908d7 | ||
|
|
6208b5e920 | ||
|
|
cb54073f65 | ||
|
|
de75c6f325 | ||
|
|
441fe8a8d9 | ||
|
|
53714a8bc9 | ||
|
|
e5ca7ed024 | ||
|
|
bcae141411 | ||
|
|
12625db783 | ||
|
|
e025a735c3 | ||
|
|
8c5c4b47df | ||
|
|
e3b702e60a | ||
|
|
09c50c183f | ||
|
|
5760726dd4 | ||
|
|
6a553ea4e8 | ||
|
|
e0685d0062 | ||
|
|
8af572c226 | ||
|
|
21ce64a418 | ||
|
|
47b2f56dad | ||
|
|
92eb6c807a | ||
|
|
23d3703a84 |
10
.ansible-lint
Normal file
10
.ansible-lint
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2024, Ansible Project
|
||||
|
||||
skip_list:
|
||||
- meta-runtime[unsupported-version] # Tis rule doesn't make any sense
|
||||
- fqcn[deep] # This rule produces false positives for files in tests/unit/plugins/action/fixtures/
|
||||
exclude_paths:
|
||||
- changelogs/
|
||||
3
.azure-pipelines/README.md
Normal file
3
.azure-pipelines/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Azure Pipelines Configuration
|
||||
|
||||
Please see the [Documentation](https://github.com/ansible/community/wiki/Testing:-Azure-Pipelines) for more information.
|
||||
279
.azure-pipelines/azure-pipelines.yml
Normal file
279
.azure-pipelines/azure-pipelines.yml
Normal file
@@ -0,0 +1,279 @@
|
||||
---
|
||||
trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
schedules:
|
||||
- cron: "0 9 * * *"
|
||||
displayName: Nightly
|
||||
always: true
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- stable-*
|
||||
|
||||
variables:
|
||||
- name: checkoutPath
|
||||
value: ansible_collections/ansible/posix
|
||||
- name: coverageBranches
|
||||
value: main
|
||||
- name: pipelinesCoverage
|
||||
value: coverage
|
||||
- name: entryPoint
|
||||
value: tests/utils/shippable/shippable.sh
|
||||
- name: fetchDepth
|
||||
value: 0
|
||||
|
||||
resources:
|
||||
containers:
|
||||
- container: default
|
||||
image: quay.io/ansible/azure-pipelines-test-container:7.0.0
|
||||
|
||||
pool: Standard
|
||||
|
||||
stages:
|
||||
- stage: Sanity_devel
|
||||
displayName: Ansible devel sanity
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: devel/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_19
|
||||
displayName: Ansible 2.19 sanitay & Units & Lint
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: 2.19/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_18
|
||||
displayName: Ansible 2.18 sanity & Units & Lint
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: 2.18/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_17
|
||||
displayName: Ansible 2.17 sanity & Units & Lint
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: 2.17/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
- name: Lint
|
||||
test: lint
|
||||
- stage: Sanity_2_16
|
||||
displayName: Ansible 2.16 sanity & Units & Lint
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
nameFormat: "{0}"
|
||||
testFormat: 2.16/{0}
|
||||
targets:
|
||||
- name: Sanity
|
||||
test: sanity
|
||||
- name: Units
|
||||
test: units
|
||||
## Docker
|
||||
- stage: Docker_devel
|
||||
displayName: Docker devel
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: devel/linux/{0}/1
|
||||
targets:
|
||||
- name: Fedora 42
|
||||
test: fedora42
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
- stage: Docker_2_19
|
||||
displayName: Docker 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.19/linux/{0}/1
|
||||
targets:
|
||||
- name: Fedora 41
|
||||
test: fedora41
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
- stage: Docker_2_18
|
||||
displayName: Docker 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/linux/{0}/1
|
||||
targets:
|
||||
- name: Fedora 40
|
||||
test: fedora40
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- name: Ubuntu 24.04
|
||||
test: ubuntu2404
|
||||
- stage: Docker_2_17
|
||||
displayName: Docker 2.17
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.17/linux/{0}/1
|
||||
targets:
|
||||
- name: Fedora 39
|
||||
test: fedora39
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
- stage: Docker_2_16
|
||||
displayName: Docker 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.16/linux/{0}/1
|
||||
targets:
|
||||
- name: CentOS 7
|
||||
test: centos7
|
||||
- name: Fedora 38
|
||||
test: fedora38
|
||||
- name: Ubuntu 22.04
|
||||
test: ubuntu2204
|
||||
|
||||
## Remote
|
||||
- stage: Remote_devel
|
||||
displayName: Remote devel
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: devel/{0}/1
|
||||
targets:
|
||||
- name: RHEL 10.0
|
||||
test: rhel/10.0
|
||||
- name: RHEL 9.6
|
||||
test: rhel/9.6
|
||||
- name: FreeBSD 14.3
|
||||
test: freebsd/14.3
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- stage: Remote_2_19
|
||||
displayName: Remote 2.19
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.19/{0}/1
|
||||
targets:
|
||||
- name: RHEL 10.0
|
||||
test: rhel/10.0
|
||||
- name: RHEL 9.5
|
||||
test: rhel/9.5
|
||||
- name: FreeBSD 14.2
|
||||
test: freebsd/14.2
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- stage: Remote_2_18
|
||||
displayName: Remote 2.18
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.18/{0}/1
|
||||
targets:
|
||||
- name: RHEL 9.4
|
||||
test: rhel/9.4
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- stage: Remote_2_17
|
||||
displayName: Remote 2.17
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.17/{0}/1
|
||||
targets:
|
||||
- name: RHEL 9.3
|
||||
test: rhel/9.3
|
||||
- name: FreeBSD 13.5
|
||||
test: freebsd/13.5
|
||||
- stage: Remote_2_16
|
||||
displayName: Remote 2.16
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: templates/matrix.yml
|
||||
parameters:
|
||||
testFormat: 2.16/{0}/1
|
||||
targets:
|
||||
- name: RHEL 8.8
|
||||
test: rhel/8.8
|
||||
- name: RHEL 9.2
|
||||
test: rhel/9.2
|
||||
|
||||
## Finally
|
||||
|
||||
- stage: Summary
|
||||
condition: succeededOrFailed()
|
||||
dependsOn:
|
||||
- Sanity_2_16
|
||||
- Remote_2_16
|
||||
- Docker_2_16
|
||||
- Sanity_2_17
|
||||
- Remote_2_17
|
||||
- Docker_2_17
|
||||
- Sanity_2_18
|
||||
- Remote_2_18
|
||||
- Docker_2_18
|
||||
- Sanity_2_19
|
||||
- Remote_2_19
|
||||
- Docker_2_19
|
||||
- Sanity_devel
|
||||
- Remote_devel
|
||||
- Docker_devel
|
||||
jobs:
|
||||
- template: templates/coverage.yml
|
||||
20
.azure-pipelines/scripts/aggregate-coverage.sh
Executable file
20
.azure-pipelines/scripts/aggregate-coverage.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Aggregate code coverage results for later processing.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
agent_temp_directory="$1"
|
||||
|
||||
PATH="${PWD}/bin:${PATH}"
|
||||
|
||||
mkdir "${agent_temp_directory}/coverage/"
|
||||
|
||||
options=(--venv --venv-system-site-packages --color -v)
|
||||
|
||||
ansible-test coverage combine --export "${agent_temp_directory}/coverage/" "${options[@]}"
|
||||
|
||||
if ansible-test coverage analyze targets generate --help >/dev/null 2>&1; then
|
||||
# Only analyze coverage if the installed version of ansible-test supports it.
|
||||
# Doing so allows this script to work unmodified for multiple Ansible versions.
|
||||
ansible-test coverage analyze targets generate "${agent_temp_directory}/coverage/coverage-analyze-targets.json" "${options[@]}"
|
||||
fi
|
||||
60
.azure-pipelines/scripts/combine-coverage.py
Executable file
60
.azure-pipelines/scripts/combine-coverage.py
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Combine coverage data from multiple jobs, keeping the data only from the most recent attempt from each job.
|
||||
Coverage artifacts must be named using the format: "Coverage $(System.JobAttempt) {StableUniqueNameForEachJob}"
|
||||
The recommended coverage artifact name format is: Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)
|
||||
Keep in mind that Azure Pipelines does not enforce unique job display names (only names).
|
||||
It is up to pipeline authors to avoid name collisions when deviating from the recommended format.
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Main program entry point."""
|
||||
source_directory = sys.argv[1]
|
||||
|
||||
if '/ansible_collections/' in os.getcwd():
|
||||
output_path = "tests/output"
|
||||
else:
|
||||
output_path = "test/results"
|
||||
|
||||
destination_directory = os.path.join(output_path, 'coverage')
|
||||
|
||||
if not os.path.exists(destination_directory):
|
||||
os.makedirs(destination_directory)
|
||||
|
||||
jobs = {}
|
||||
count = 0
|
||||
|
||||
for name in os.listdir(source_directory):
|
||||
match = re.search('^Coverage (?P<attempt>[0-9]+) (?P<label>.+)$', name)
|
||||
label = match.group('label')
|
||||
attempt = int(match.group('attempt'))
|
||||
jobs[label] = max(attempt, jobs.get(label, 0))
|
||||
|
||||
for label, attempt in jobs.items():
|
||||
name = 'Coverage {attempt} {label}'.format(label=label, attempt=attempt)
|
||||
source = os.path.join(source_directory, name)
|
||||
source_files = os.listdir(source)
|
||||
|
||||
for source_file in source_files:
|
||||
source_path = os.path.join(source, source_file)
|
||||
destination_path = os.path.join(destination_directory, source_file + '.' + label)
|
||||
print('"%s" -> "%s"' % (source_path, destination_path))
|
||||
shutil.copyfile(source_path, destination_path)
|
||||
count += 1
|
||||
|
||||
print('Coverage file count: %d' % count)
|
||||
print('##vso[task.setVariable variable=coverageFileCount]%d' % count)
|
||||
print('##vso[task.setVariable variable=outputPath]%s' % output_path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
24
.azure-pipelines/scripts/process-results.sh
Executable file
24
.azure-pipelines/scripts/process-results.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check the test results and set variables for use in later steps.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
if [[ "$PWD" =~ /ansible_collections/ ]]; then
|
||||
output_path="tests/output"
|
||||
else
|
||||
output_path="test/results"
|
||||
fi
|
||||
|
||||
echo "##vso[task.setVariable variable=outputPath]${output_path}"
|
||||
|
||||
if compgen -G "${output_path}"'/junit/*.xml' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveTestResults]true"
|
||||
fi
|
||||
|
||||
if compgen -G "${output_path}"'/bot/ansible-test-*' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveBotResults]true"
|
||||
fi
|
||||
|
||||
if compgen -G "${output_path}"'/coverage/*' > /dev/null; then
|
||||
echo "##vso[task.setVariable variable=haveCoverageData]true"
|
||||
fi
|
||||
27
.azure-pipelines/scripts/publish-codecov.sh
Executable file
27
.azure-pipelines/scripts/publish-codecov.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
# Upload code coverage reports to codecov.io.
|
||||
# Multiple coverage files from multiple languages are accepted and aggregated after upload.
|
||||
# Python coverage, as well as PowerShell and Python stubs can all be uploaded.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
output_path="$1"
|
||||
|
||||
curl --silent --show-error https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh > codecov.sh
|
||||
|
||||
for file in "${output_path}"/reports/coverage*.xml; do
|
||||
name="${file}"
|
||||
name="${name##*/}" # remove path
|
||||
name="${name##coverage=}" # remove 'coverage=' prefix if present
|
||||
name="${name%.xml}" # remove '.xml' suffix
|
||||
|
||||
bash codecov.sh \
|
||||
-f "${file}" \
|
||||
-n "${name}" \
|
||||
-X coveragepy \
|
||||
-X gcov \
|
||||
-X fix \
|
||||
-X search \
|
||||
-X xcode \
|
||||
|| echo "Failed to upload code coverage report to codecov.io: ${file}"
|
||||
done
|
||||
15
.azure-pipelines/scripts/report-coverage.sh
Executable file
15
.azure-pipelines/scripts/report-coverage.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generate code coverage reports for uploading to Azure Pipelines and codecov.io.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
PATH="${PWD}/bin:${PATH}"
|
||||
|
||||
if ! ansible-test --help >/dev/null 2>&1; then
|
||||
# Install the devel version of ansible-test for generating code coverage reports.
|
||||
# This is only used by Ansible Collections, which are typically tested against multiple Ansible versions (in separate jobs).
|
||||
# Since a version of ansible-test is required that can work the output from multiple older releases, the devel version is used.
|
||||
pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
|
||||
fi
|
||||
|
||||
ansible-test coverage xml --stub --venv --venv-system-site-packages --color -v
|
||||
34
.azure-pipelines/scripts/run-tests.sh
Executable file
34
.azure-pipelines/scripts/run-tests.sh
Executable file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# Configure the test environment and run the tests.
|
||||
|
||||
set -o pipefail -eu
|
||||
|
||||
entry_point="$1"
|
||||
test="$2"
|
||||
read -r -a coverage_branches <<< "$3" # space separated list of branches to run code coverage on for scheduled builds
|
||||
|
||||
export COMMIT_MESSAGE
|
||||
export COMPLETE
|
||||
export COVERAGE
|
||||
export IS_PULL_REQUEST
|
||||
|
||||
if [ "${SYSTEM_PULLREQUEST_TARGETBRANCH:-}" ]; then
|
||||
IS_PULL_REQUEST=true
|
||||
COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD^2)
|
||||
else
|
||||
IS_PULL_REQUEST=
|
||||
COMMIT_MESSAGE=$(git log --format=%B -n 1 HEAD)
|
||||
fi
|
||||
|
||||
COMPLETE=
|
||||
COVERAGE=
|
||||
|
||||
if [ "${BUILD_REASON}" = "Schedule" ]; then
|
||||
COMPLETE=yes
|
||||
|
||||
if printf '%s\n' "${coverage_branches[@]}" | grep -q "^${BUILD_SOURCEBRANCHNAME}$"; then
|
||||
COVERAGE=yes
|
||||
fi
|
||||
fi
|
||||
|
||||
"${entry_point}" "${test}" 2>&1 | "$(dirname "$0")/time-command.py"
|
||||
25
.azure-pipelines/scripts/time-command.py
Executable file
25
.azure-pipelines/scripts/time-command.py
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python
|
||||
"""Prepends a relative timestamp to each input line from stdin and writes it to stdout."""
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def main():
|
||||
"""Main program entry point."""
|
||||
start = time.time()
|
||||
|
||||
sys.stdin.reconfigure(errors='surrogateescape')
|
||||
sys.stdout.reconfigure(errors='surrogateescape')
|
||||
|
||||
for line in sys.stdin:
|
||||
seconds = time.time() - start
|
||||
sys.stdout.write('%02d:%02d %s' % (seconds // 60, seconds % 60, line))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
39
.azure-pipelines/templates/coverage.yml
Normal file
39
.azure-pipelines/templates/coverage.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
# This template adds a job for processing code coverage data.
|
||||
# It will upload results to Azure Pipelines and codecov.io.
|
||||
# Use it from a job stage that completes after all other jobs have completed.
|
||||
# This can be done by placing it in a separate summary stage that runs after the test stage(s) have completed.
|
||||
|
||||
jobs:
|
||||
- job: Coverage
|
||||
displayName: Code Coverage
|
||||
container: default
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: Download Coverage Data
|
||||
inputs:
|
||||
path: coverage/
|
||||
patterns: "Coverage */*=coverage.combined"
|
||||
- bash: .azure-pipelines/scripts/combine-coverage.py coverage/
|
||||
displayName: Combine Coverage Data
|
||||
- bash: .azure-pipelines/scripts/report-coverage.sh
|
||||
displayName: Generate Coverage Report
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
# Azure Pipelines only accepts a single coverage data file.
|
||||
# That means only Python or PowerShell coverage can be uploaded, but not both.
|
||||
# Set the "pipelinesCoverage" variable to determine which type is uploaded.
|
||||
# Use "coverage" for Python and "coverage-powershell" for PowerShell.
|
||||
summaryFileLocation: "$(outputPath)/reports/$(pipelinesCoverage).xml"
|
||||
displayName: Publish to Azure Pipelines
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
- bash: .azure-pipelines/scripts/publish-codecov.sh "$(outputPath)"
|
||||
displayName: Publish to codecov.io
|
||||
condition: gt(variables.coverageFileCount, 0)
|
||||
continueOnError: true
|
||||
55
.azure-pipelines/templates/matrix.yml
Normal file
55
.azure-pipelines/templates/matrix.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
# This template uses the provided targets and optional groups to generate a matrix which is then passed to the test template.
|
||||
# If this matrix template does not provide the required functionality, consider using the test template directly instead.
|
||||
|
||||
parameters:
|
||||
# A required list of dictionaries, one per test target.
|
||||
# Each item in the list must contain a "test" or "name" key.
|
||||
# Both may be provided. If one is omitted, the other will be used.
|
||||
- name: targets
|
||||
type: object
|
||||
|
||||
# An optional list of values which will be used to multiply the targets list into a matrix.
|
||||
# Values can be strings or numbers.
|
||||
- name: groups
|
||||
type: object
|
||||
default: []
|
||||
|
||||
# An optional format string used to generate the job name.
|
||||
# - {0} is the name of an item in the targets list.
|
||||
- name: nameFormat
|
||||
type: string
|
||||
default: "{0}"
|
||||
|
||||
# An optional format string used to generate the test name.
|
||||
# - {0} is the name of an item in the targets list.
|
||||
- name: testFormat
|
||||
type: string
|
||||
default: "{0}"
|
||||
|
||||
# An optional format string used to add the group to the job name.
|
||||
# {0} is the formatted name of an item in the targets list.
|
||||
# {{1}} is the group -- be sure to include the double "{{" and "}}".
|
||||
- name: nameGroupFormat
|
||||
type: string
|
||||
default: "{0} - {{1}}"
|
||||
|
||||
# An optional format string used to add the group to the test name.
|
||||
# {0} is the formatted test of an item in the targets list.
|
||||
# {{1}} is the group -- be sure to include the double "{{" and "}}".
|
||||
- name: testGroupFormat
|
||||
type: string
|
||||
default: "{0}/{{1}}"
|
||||
|
||||
jobs:
|
||||
- template: test.yml
|
||||
parameters:
|
||||
jobs:
|
||||
- ${{ if eq(length(parameters.groups), 0) }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(parameters.nameFormat, coalesce(target.name, target.test)) }}
|
||||
test: ${{ format(parameters.testFormat, coalesce(target.test, target.name)) }}
|
||||
- ${{ if not(eq(length(parameters.groups), 0)) }}:
|
||||
- ${{ each group in parameters.groups }}:
|
||||
- ${{ each target in parameters.targets }}:
|
||||
- name: ${{ format(format(parameters.nameGroupFormat, parameters.nameFormat), coalesce(target.name, target.test), group) }}
|
||||
test: ${{ format(format(parameters.testGroupFormat, parameters.testFormat), coalesce(target.test, target.name), group) }}
|
||||
45
.azure-pipelines/templates/test.yml
Normal file
45
.azure-pipelines/templates/test.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# This template uses the provided list of jobs to create test one or more test jobs.
|
||||
# It can be used directly if needed, or through the matrix template.
|
||||
|
||||
parameters:
|
||||
# A required list of dictionaries, one per test job.
|
||||
# Each item in the list must contain a "job" and "name" key.
|
||||
- name: jobs
|
||||
type: object
|
||||
|
||||
jobs:
|
||||
- ${{ each job in parameters.jobs }}:
|
||||
- job: test_${{ replace(replace(replace(job.test, '/', '_'), '.', '_'), '-', '_') }}
|
||||
displayName: ${{ job.name }}
|
||||
container: default
|
||||
workspace:
|
||||
clean: all
|
||||
steps:
|
||||
- checkout: self
|
||||
fetchDepth: $(fetchDepth)
|
||||
path: $(checkoutPath)
|
||||
- bash: .azure-pipelines/scripts/run-tests.sh "$(entryPoint)" "${{ job.test }}" "$(coverageBranches)"
|
||||
displayName: Run Tests
|
||||
- bash: .azure-pipelines/scripts/process-results.sh
|
||||
condition: succeededOrFailed()
|
||||
displayName: Process Results
|
||||
- bash: .azure-pipelines/scripts/aggregate-coverage.sh "$(Agent.TempDirectory)"
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Aggregate Coverage Data
|
||||
- task: PublishTestResults@2
|
||||
condition: eq(variables.haveTestResults, 'true')
|
||||
inputs:
|
||||
testResultsFiles: "$(outputPath)/junit/*.xml"
|
||||
displayName: Publish Test Results
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveBotResults, 'true')
|
||||
displayName: Publish Bot Results
|
||||
inputs:
|
||||
targetPath: "$(outputPath)/bot/"
|
||||
artifactName: "Bot $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
- task: PublishPipelineArtifact@1
|
||||
condition: eq(variables.haveCoverageData, 'true')
|
||||
displayName: Publish Coverage Data
|
||||
inputs:
|
||||
targetPath: "$(Agent.TempDirectory)/coverage/"
|
||||
artifactName: "Coverage $(System.JobAttempt) $(System.StageDisplayName) $(System.JobDisplayName)"
|
||||
6
.github/BOTMETA.yml
vendored
6
.github/BOTMETA.yml
vendored
@@ -1,7 +1,8 @@
|
||||
---
|
||||
automerge: false
|
||||
files:
|
||||
$module_utils/ismount.py:
|
||||
labels: ismount
|
||||
$module_utils/mount.py:
|
||||
labels: mount
|
||||
$modules/acl.py:
|
||||
authors: astorije bcoca
|
||||
labels: acl
|
||||
@@ -39,7 +40,6 @@ files:
|
||||
labels: debug
|
||||
$plugins/patch.py:
|
||||
labels: patch
|
||||
$plugins/skippy.py:
|
||||
$plugins/synchronize.py:
|
||||
labels: synchronize
|
||||
$plugins/timer.py:
|
||||
|
||||
61
.github/settings.yml
vendored
61
.github/settings.yml
vendored
@@ -1,61 +0,0 @@
|
||||
###
|
||||
# https://probot.github.io/apps/settings/
|
||||
#
|
||||
|
||||
# DO NOT MODIFY
|
||||
# this is a copy of https://github.com/gundalow-collection/.github/blob/master/.github/settings.yml
|
||||
# Work around till https://github.com/probot/settings/pull/179 is merged
|
||||
|
||||
|
||||
|
||||
repository:
|
||||
# See https://developer.github.com/v3/repos/#edit for all available settings.
|
||||
has_issues: true
|
||||
has_wiki: false
|
||||
has_pages: false
|
||||
default_branch: devel
|
||||
allow_squash_merge: true
|
||||
allow_merge_commit: false
|
||||
allow_rebase_merge: true
|
||||
|
||||
# Labels: define labels for Issues and Pull Requests
|
||||
labels:
|
||||
- name: bug
|
||||
color: fbca04
|
||||
description: This issue/PR relates to a bug.
|
||||
- name: feature
|
||||
description: This issue/PR relates to a feature request.
|
||||
color: 006b75
|
||||
- name: migrated_from_ansible_ansible
|
||||
color: 5319e7
|
||||
description: This issue/PR was moved from gh/ansible/ansible
|
||||
|
||||
branches:
|
||||
- name: master
|
||||
# https://developer.github.com/v3/repos/branches/#update-branch-protection
|
||||
# Branch Protection settings. Set to null to disable
|
||||
protection:
|
||||
# Required. Require at least one approving review on a pull request, before merging. Set to null to disable.
|
||||
required_pull_request_reviews:
|
||||
# The number of approvals required. (1-6)
|
||||
required_approving_review_count: 1
|
||||
# Dismiss approved reviews automatically when a new commit is pushed.
|
||||
dismiss_stale_reviews: true
|
||||
# Blocks merge until code owners have reviewed.
|
||||
require_code_owner_reviews: true
|
||||
# Specify which users and teams can dismiss pull request reviews. Pass an empty dismissal_restrictions object to disable. User and team dismissal_restrictions are only available for organization-owned repositories. Omit this parameter for personal repositories.
|
||||
dismissal_restrictions:
|
||||
users: []
|
||||
teams: []
|
||||
# Required. Require status checks to pass before merging. Set to null to disable
|
||||
required_status_checks:
|
||||
# Required. Require branches to be up to date before merging.
|
||||
strict: true
|
||||
# Required. The list of status checks to require in order to merge into this branch
|
||||
contexts: []
|
||||
# Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable.
|
||||
enforce_admins: true
|
||||
# Required. Restrict who can push to this branch. Team and user restrictions are only available for organization-owned repositories. Set to null to disable.
|
||||
#restrictions:
|
||||
# users: []
|
||||
# teams: []
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -384,4 +384,7 @@ $RECYCLE.BIN/
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Antsibull-changelog
|
||||
changelogs/.plugin-cache.yaml
|
||||
|
||||
# End of https://www.gitignore.io/api/git,linux,pydev,python,windows,pycharm+all,jupyternotebook,vim,webstorm,emacs,dotenv
|
||||
|
||||
347
CHANGELOG.rst
Normal file
347
CHANGELOG.rst
Normal file
@@ -0,0 +1,347 @@
|
||||
===========================
|
||||
ansible.posix Release Notes
|
||||
===========================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
v2.0.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the major release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules and plugins
|
||||
in this collection that have been added after the release of
|
||||
``ansible.posix`` 1.6.2
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- authorized_keys - allow using absolute path to a file as a SSH key(s) source (https://github.com/ansible-collections/ansible.posix/pull/568)
|
||||
- callback plugins - Add recap information to timer, profile_roles and profile_tasks callback outputs (https://github.com/ansible-collections/ansible.posix/pull/387).
|
||||
|
||||
Breaking Changes / Porting Guide
|
||||
--------------------------------
|
||||
|
||||
- firewalld - Changed the type of forward and masquerade options from str to bool (https://github.com/ansible-collections/ansible.posix/issues/582).
|
||||
- firewalld - Changed the type of icmp_block_inversion option from str to bool (https://github.com/ansible-collections/ansible.posix/issues/586).
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- skippy - Remove skippy pluglin as it is no longer supported(https://github.com/ansible-collections/ansible.posix/issues/350).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- acl - Fixed to set ACLs on paths mounted with NFS version 4 correctly (https://github.com/ansible-collections/ansible.posix/issues/240).
|
||||
- mount - Handle ``boot`` option on Linux, NetBSD and OpenBSD correctly (https://github.com/ansible-collections/ansible.posix/issues/364).
|
||||
- mount - If a comment is appended to a fstab entry, state present creates a double-entry (https://github.com/ansible-collections/ansible.posix/issues/595).
|
||||
|
||||
v1.6.2
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the bugfix release of the stable version ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules and plugins
|
||||
in this collection that have been added after the release of
|
||||
``ansible.posix`` 1.6.1.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- backport - Drop ansible-core 2.14 and set 2.15 minimum version (https://github.com/ansible-collections/ansible.posix/issues/578).
|
||||
|
||||
v1.6.1
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the bugfix release of the stable version ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules and plugins
|
||||
in this collection that have been added after the release of
|
||||
``ansible.posix`` 1.6.1.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- acl - Fixed to set ACLs on paths mounted with NFS version 4 correctly (https://github.com/ansible-collections/ansible.posix/issues/240).
|
||||
- mount - Handle ``boot`` option on Linux, NetBSD and OpenBSD correctly (https://github.com/ansible-collections/ansible.posix/issues/364).
|
||||
- skippy - Revert removal of skippy plugin. It will be removed in version 2.0.0 (https://github.com/ansible-collections/ansible.posix/issues/573).
|
||||
|
||||
v1.6.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the minor release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules and plugins
|
||||
in this collection that have been added after the release of
|
||||
``ansible.posix`` 1.5.4.
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- Dropping support for Ansible 2.9, ansible-core 2.15 will be minimum required version for this release
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add summary_only parameter to profile_roles and profile_tasks callbacks.
|
||||
- firewalld - add functionality to set forwarding (https://github.com/ansible-collections/ansible.posix/pull/548).
|
||||
- firewalld - added offline flag implementation (https://github.com/ansible-collections/ansible.posix/pull/484)
|
||||
- firewalld - respawn module to use the system python interpreter when the ``firewall`` python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- firewalld_info - Only warn about ignored zones, when there are zones ignored.
|
||||
- firewalld_info - respawn module to use the system python interpreter when the ``firewall`` python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- mount - add no_log option for opts parameter (https://github.com/ansible-collections/ansible.posix/pull/563).
|
||||
- seboolean - respawn module to use the system python interpreter when the ``selinux`` python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- selinux - respawn module to use the system python interpreter when the ``selinux`` python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
|
||||
Removed Features (previously deprecated)
|
||||
----------------------------------------
|
||||
|
||||
- skippy - Remove skippy pluglin as it is no longer supported(https://github.com/ansible-collections/ansible.posix/issues/350).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Bugfix in the documentation regarding the path option for authorised_key(https://github.com/ansible-collections/ansible.posix/issues/483).
|
||||
- seboolean - make it work with disabled SELinux
|
||||
- synchronize - maintain proper formatting of the remote paths (https://github.com/ansible-collections/ansible.posix/pull/361).
|
||||
- sysctl - fix sysctl to work properly on symlinks (https://github.com/ansible-collections/ansible.posix/issues/111).
|
||||
|
||||
v1.5.4
|
||||
======
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- json and jsonl - Add the ``ANSIBLE_JSON_INDENT`` parameter
|
||||
- json and jsonl - Add the ``path`` attribute into the play and task output
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix sysctl integration test failing on newer versions of core. Previously NoneType was allowable, now it fails to convert to a str type.
|
||||
- Support new sanity test for the ansible-core devel branch CI test (https://github.com/ansible-collections/ansible.posix/issues/446).
|
||||
- firewall - Fix issue where adding an interface to a zone would fail when the daemon is offline
|
||||
- firewall - Fix issue where opening a specific port resulted in opening the whole protocol of the specified port
|
||||
- firewalld - Consider value of masquerade and icmp_block_inversion parameters when a boolean like value is passed
|
||||
|
||||
v1.5.2
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the minor release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules and plugins
|
||||
in this collection that have been added after the release of
|
||||
``ansible.posix`` 1.5.1.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- Add jsonl callback plugin to ansible.posix collection
|
||||
- firewalld - add `protocol` parameter
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fixed a bug where firewalld module fails to create/remove zones when the daemon is stopped
|
||||
- rhel_facts - Call exit_json with all keyword arguments
|
||||
|
||||
v1.5.1
|
||||
======
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- mount - Add ``absent_from_fstab`` state (https://github.com/ansible-collections/ansible.posix/pull/166).
|
||||
- mount - Add ``ephemeral`` value for the ``state`` parameter, that allows to mount a filesystem without altering the ``fstab`` file (https://github.com/ansible-collections/ansible.posix/pull/267).
|
||||
- r4e_rpm_ostree - new module for validating package state on RHEL for Edge
|
||||
- rhel_facts - new facts module to handle RHEL specific facts
|
||||
- rhel_rpm_ostree - new module to handle RHEL rpm-ostree specific package management functionality
|
||||
- rpm_ostree_upgrade - new module to automate rpm-ostree upgrades
|
||||
- rpm_ostree_upgrade - new module to manage upgrades for rpm-ostree based systems
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Removed contentious terminology to match reference documentation in profile_tasks.
|
||||
- firewall - Fixed to output a more complete missing library message.
|
||||
- synchronize - Fixed hosts involved in rsync require the same password
|
||||
|
||||
v1.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the minor release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
have been added after the release of ``ansible.posix`` 1.3.0.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- firewalld - Show warning message that variable type of ``masquerade`` and ``icmp_block_inversion`` will be changed from ``str`` to ``boolean`` in the future release (https://github.com/ansible-collections/ansible.posix/pull/254).
|
||||
- selinux - optionally update kernel boot params when disabling/re-enabling SELinux (https://github.com/ansible-collections/ansible.posix/pull/142).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix for whitespace in source full path causing error ```code 23) at main.c(1330) [sender=3.2.3]``` (https://github.com/ansible-collections/ansible.posix/pull/278)
|
||||
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
|
||||
- Use vendored version of ``distutils.version`` instead of the deprecated Python standard library to address PEP 632 (https://github.com/ansible-collections/ansible.posix/issues/303).
|
||||
- firewalld - Correct usage of queryForwardPort (https://github.com/ansible-collections/ansible.posix/issues/247).
|
||||
- firewalld - Refine the handling of exclusive options (https://github.com/ansible-collections/ansible.posix/issues/255).
|
||||
- mount - add a newline at the end of line in ``fstab`` (https://github.com/ansible-collections/ansible.posix/issues/210).
|
||||
- profile_tasks - Correctly calculate task execution time with serial execution (https://github.com/ansible-collections/ansible.posix/issues/83).
|
||||
- seboolean - add ``python3-libsemanage`` package dependency for RHEL8+ systems.
|
||||
|
||||
v1.3.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the minor release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
have been added after the release of ``ansible.posix`` 1.2.0.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- acl - add new alias ``recurse`` for ``recursive`` parameter (https://github.com/ansible-collections/ansible.posix/issues/124).
|
||||
- added 2.11 branch to test matrix, added ignore-2.12.txt.
|
||||
- authorized_key - add ``no_log=False`` in ``argument_spec`` to clear false-positives of ``no-log-needed`` (https://github.com/ansible-collections/ansible.posix/pull/156).
|
||||
- authorized_key - add a list of valid key types (https://github.com/ansible-collections/ansible.posix/issues/134).
|
||||
- mount - Change behavior of ``boot`` option to set ``noauto`` on BSD nodes (https://github.com/ansible-collections/ansible.posix/issues/28).
|
||||
- mount - Change behavior of ``boot`` option to set ``noauto`` on Linux nodes (https://github.com/ansible-collections/ansible.posix/issues/28).
|
||||
- mount - add ``no_log=False`` in ``argument_spec`` to clear false-positives of ``no-log-needed`` (https://github.com/ansible-collections/ansible.posix/pull/156).
|
||||
- mount - returns ``backup_file`` value when a backup fstab is created.
|
||||
- synchronize - add ``delay_updates`` option (https://github.com/ansible-collections/ansible.posix/issues/157).
|
||||
- synchronize - fix typo (https://github.com/ansible-collections/ansible.posix/pull/198).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Synchronize module not recognizing remote ssh key (https://github.com/ansible-collections/ansible.posix/issues/24).
|
||||
- Synchronize not using quotes around arguments like --out-format (https://github.com/ansible-collections/ansible.posix/issues/190).
|
||||
- at - append line-separator to the end of the ``command`` (https://github.com/ansible-collections/ansible.posix/issues/169).
|
||||
- csh - define ``ECHO`` and ``COMMAND_SEP`` (https://github.com/ansible-collections/ansible.posix/issues/204).
|
||||
- firewalld - enable integration after migration (https://github.com/ansible-collections/ansible.posix/pull/239).
|
||||
- firewalld - ensure idempotency with firewalld 0.9.3 (https://github.com/ansible-collections/ansible.posix/issues/179).
|
||||
- firewalld - fix setting zone target to ``%%REJECT%%`` (https://github.com/ansible-collections/ansible.posix/pull/215).
|
||||
- mount - Handle ``boot`` option on Solaris correctly (https://github.com/ansible-collections/ansible.posix/issues/184).
|
||||
- synchronize - add ``community.podman.podman`` to the list of supported connection plugins (https://github.com/ansible-community/molecule-podman/issues/45).
|
||||
- synchronize - complete podman support for synchronize module.
|
||||
- synchronize - properly quote rsync CLI parameters (https://github.com/ansible-collections/ansible.posix/pull/241).
|
||||
- synchronize - replace removed ``ansible_ssh_user`` by ``ansible_user`` everywhere; do the same for ``ansible_ssh_port`` and ``ansible_ssh_host`` (https://github.com/ansible-collections/ansible.posix/issues/60).
|
||||
- synchronize - use SSH args from SSH connection plugin (https://github.com/ansible-collections/ansible.posix/issues/222).
|
||||
- synchronize - use become_user when invoking rsync on remote with sudo (https://github.com/ansible-collections/ansible.posix/issues/186).
|
||||
- sysctl - modifying conditional check for docker to fix tests being skipped (https://github.com/ansible-collections/ansible.posix/pull/226).
|
||||
|
||||
v1.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
This is the minor release of the ``ansible.posix`` collection.
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
have been added after the release of ``ansible.posix`` 1.1.0.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- firewalld - bring the ``target`` feature back (https://github.com/ansible-collections/ansible.posix/issues/112).
|
||||
- fix sanity test for various modules.
|
||||
- synchronize - add the ``ssh_connection_multiplexing`` option to allow SSH connection multiplexing (https://github.com/ansible/ansible/issues/24365).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- at - add AIX support (https://github.com/ansible-collections/ansible.posix/pull/99).
|
||||
- synchronize - add ``community.docker.docker`` to the list of supported transports (https://github.com/ansible-collections/ansible.posix/issues/132).
|
||||
- synchronize - do not prepend PWD when path is in form user@server:path or server:path (https://github.com/ansible-collections/ansible.posix/pull/118).
|
||||
- synchronize - fix for private_key overriding in synchronize module.
|
||||
- sysctl - do not persist sysctl when value is invalid (https://github.com/ansible-collections/ansible.posix/pull/101).
|
||||
|
||||
v1.1.1
|
||||
======
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- skippy - fixed the deprecation warning (by date) for skippy callback plugin
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fix synchronize to work with renamed docker and buildah connection plugins.
|
||||
|
||||
v1.1.0
|
||||
======
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- firewalld - add firewalld module to ansible.posix collection
|
||||
|
||||
v1.0.0
|
||||
======
|
||||
|
||||
Major Changes
|
||||
-------------
|
||||
|
||||
- Bootstrap Collection (https://github.com/ansible-collections/ansible.posix/pull/1).
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- CI should use devel (https://github.com/ansible-collections/ansible.posix/pull/6).
|
||||
- Enable tests for at, patch and synchronize modules (https://github.com/ansible-collections/ansible.posix/pull/5).
|
||||
- Enabled tags in galaxy.yml (https://github.com/ansible-collections/ansible.posix/issues/18).
|
||||
- Migrate hacking/cgroup_perf_recap_graph.py to this collection, since the cgroup_perf_recap callback lives here.
|
||||
- Remove license key from galaxy.yml.
|
||||
- Remove sanity jobs from shippable (https://github.com/ansible-collections/ansible.posix/pull/43).
|
||||
- Removed ANSIBLE_METADATA from all the modules.
|
||||
- Revert "Enable at, patch and synchronize tests (https://github.com/ansible-collections/ansible.posix/pull/5)".
|
||||
- Update EXAMPLES section in modules to use FQCN.
|
||||
- Update README.md (https://github.com/ansible-collections/ansible.posix/pull/4/).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Allow unsetting existing environment vars via environment by specifying a null value (https://github.com/ansible/ansible/pull/68236).
|
||||
- Mount - Handle remount with new options (https://github.com/ansible/ansible/issues/59460).
|
||||
- Profile_tasks - result was a odict_items which is not subscriptable, so the slicing was failing (https://github.com/ansible/ansible/issues/59059).
|
||||
- Revert "mount - Check if src exists before mounted (ansible/ansible#61752)".
|
||||
- Typecast results before use in profile_tasks callback (https://github.com/ansible/ansible/issues/69563).
|
||||
- authorized_keys - Added FIDO2 security keys (https://github.com/ansible-collections/ansible.posix/issues/17).
|
||||
- authorized_keys - fix inconsistent return value for check mode (https://github.com/ansible-collections/ansible.posix/issues/37)
|
||||
- json callback - Fix host result to task references in the resultant JSON output for non-lockstep strategy plugins such as free (https://github.com/ansible/ansible/issues/65931)
|
||||
- mount - fix issues with ismount module_util pathing for Ansible 2.9 (fixes https://github.com/ansible-collections/ansible.posix/issues/21)
|
||||
- patch - fix FQCN usage for action plugin (https://github.com/ansible-collections/ansible.posix/issues/11)
|
||||
- selinux - add missing configuration keys for /etc/selinux/config (https://github.com/ansible-collections/ansible.posix/issues/23)
|
||||
- synchronize - fix FQCN usage for action plugin (https://github.com/ansible-collections/ansible.posix/issues/11)
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- acl - Set and retrieve file ACL information.
|
||||
- at - Schedule the execution of a command or script file via the at command
|
||||
- authorized_key - Adds or removes an SSH authorized key
|
||||
- mount - Control active and configured mount points
|
||||
- patch - Apply patch files using the GNU patch tool
|
||||
- seboolean - Toggles SELinux booleans
|
||||
- selinux - Change policy and state of SELinux
|
||||
- synchronize - A wrapper around rsync to make common tasks in your playbooks quick and easy
|
||||
- sysctl - Manage entries in sysctl.conf.
|
||||
48
PSF-license.txt
Normal file
48
PSF-license.txt
Normal file
@@ -0,0 +1,48 @@
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
109
README.md
109
README.md
@@ -1,58 +1,113 @@
|
||||
# ansible.posix
|
||||
<!-- Add CI and code coverage badges here. Samples included below. -->
|
||||
[]() [](https://codecov.io/gh/ansible-collections/ansible.posix)
|
||||
[](https://dev.azure.com/ansible/ansible.posix/_build?definitionId=26)
|
||||
[]() <!--[](https://codecov.io/gh/ansible-collections/ansible.posix)-->
|
||||
|
||||
## Communication
|
||||
|
||||
* Join the Ansible forum:
|
||||
* [Get Help](https://forum.ansible.com/c/help/6): get help or help others.
|
||||
* [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts.
|
||||
* [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events.
|
||||
|
||||
## Description
|
||||
|
||||
<!-- Describe the collection and why a user would want to use it. What does the collection do? -->
|
||||
An Ansible Collection of modules and plugins that target POSIX UNIX/Linux and derivative Operating Systems.
|
||||
|
||||
## Tested with Ansible
|
||||
## Requirements
|
||||
|
||||
<!-- List the versions of Ansible the collection has been tested with. Must match what is in galaxy.yml. -->
|
||||
* Python:
|
||||
* The Python interpreter version must meet Ansible Core's requirements.
|
||||
* Ansible Core:
|
||||
- ansible-core 2.16 or later
|
||||
|
||||
* ansible-base 2.10 (devel)
|
||||
## Installation
|
||||
|
||||
## External requirements
|
||||
Before using this collection, you need to install it with the Ansible Galaxy command-line tool:
|
||||
|
||||
None
|
||||
```shell
|
||||
ansible-galaxy collection install ansible.posix
|
||||
```
|
||||
|
||||
## Included content
|
||||
You can also include it in a requirements.yml file and install it with ansible-galaxy collection install -r requirements.yml, using the format:
|
||||
|
||||
<!-- Galaxy will eventually list the module docs within the UI, but until that is ready, you may need to either describe your plugins etc here, or point to an external docsite to cover that information. -->
|
||||
|
||||
## Using this collection
|
||||
```yaml
|
||||
collections:
|
||||
- name: ansible.posix
|
||||
```
|
||||
|
||||
<!--Include some quick examples that cover the most common use cases for your collection content. -->
|
||||
Note that if you install any collections from Ansible Galaxy, they will not be upgraded automatically when you upgrade the Ansible package.
|
||||
To upgrade the collection to the latest available version, run the following command:
|
||||
|
||||
See [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details.
|
||||
```shell
|
||||
ansible-galaxy collection install ansible.posix --upgrade
|
||||
```
|
||||
|
||||
## Contributing to this collection
|
||||
You can also install a specific version of the collection, for example, if you need to downgrade when something is broken in the latest version (please report an issue in this repository). Use the following syntax to install version 2.0.0:
|
||||
|
||||
<!--Describe how the community can contribute to your collection. At a minimum, include how and where users can create issues to report problems or request features for this collection. List contribution requirements, including preferred workflows and necessary testing, so you can benefit from community PRs. If you are following general Ansible contributor guidelines, you can link to - [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html). -->
|
||||
```shell
|
||||
ansible-galaxy collection install ansible.posix:==2.0.0
|
||||
```
|
||||
|
||||
See [using Ansible collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more details.
|
||||
|
||||
* The Ansible [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn): used to announce releases and important changes.
|
||||
|
||||
For more information about communication, see the [Ansible communication guide](https://docs.ansible.com/ansible/devel/community/communication.html).
|
||||
|
||||
## Use Cases
|
||||
|
||||
You can see the general use-cases as an example by `ansible-doc` command like below.
|
||||
|
||||
For example, ansible.posix.firewalld module:
|
||||
```shell
|
||||
ansible-doc ansible.posix.firewalld
|
||||
```
|
||||
|
||||
Also, if you want to confirm the plugins descriptions, you can follow the following option with `ansible-doc` command:
|
||||
|
||||
For example, ansible.posix.profile_tasks callback plugin:
|
||||
```shell
|
||||
ansible-doc -t callback ansible.posix.profile_tasks
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
The following ansible-core versions have been tested with this collection:
|
||||
|
||||
- ansible-core 2.20 (devel)
|
||||
- ansible-core 2.19 (stable) *
|
||||
- ansible-core 2.18 (stable)
|
||||
- ansible-core 2.17 (stable)
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome community contributions to this collection. For more details, see [Contributing to Ansible-maintained collections](https://docs.ansible.com/ansible/devel/community/contributing_maintained_collections.html#contributing-maintained-collections) for complete details.
|
||||
|
||||
* [Issues](https://github.com/ansible-collections/ansible.posix/issues)
|
||||
* [Pull Requests](https://github.com/ansible-collections/ansible.posix/pulls)
|
||||
* [Ansible Community Guide](https://docs.ansible.com/ansible/latest/community/index.html)
|
||||
|
||||
## Release notes
|
||||
|
||||
* 0.1.1 Initial stable build
|
||||
* 0.1.0 Internal only build
|
||||
## Support
|
||||
|
||||
## Roadmap
|
||||
See [Communication](#Communication) section.
|
||||
|
||||
<!-- Optional. Include the roadmap for this collection, and the proposed release/versioning strategy so users can anticipate the upgrade/update cycle. -->
|
||||
## Release Notes and Roadmap
|
||||
|
||||
## More information
|
||||
See [changelog](https://github.com/ansible-collections/ansible.posix/blob/main/CHANGELOG.rst) for more details.
|
||||
|
||||
<!-- List out where the user can find additional information, such as working group meeting times, slack/IRC channels, or documentation for the product this collection automates. At a minimum, link to: -->
|
||||
## Related Information
|
||||
|
||||
- [Ansible Collection overview](https://github.com/ansible-collections/overview)
|
||||
- [Ansible User guide](https://docs.ansible.com/ansible/latest/user_guide/index.html)
|
||||
- [Ansible Developer guide](https://docs.ansible.com/ansible/latest/dev_guide/index.html)
|
||||
- [Ansible Community code of conduct](https://docs.ansible.com/ansible/latest/community/code_of_conduct.html)
|
||||
This document was written using the following [template](https://access.redhat.com/articles/7068606).
|
||||
|
||||
## Licensing
|
||||
The README has been carefully prepared to cover the [community template](https://github.com/ansible-collections/collection_template/blob/main/README.md), but if you find any problems, please file a [documentation issue](https://github.com/ansible-collections/ansible.posix/issues/new?assignees=&labels=&projects=&template=documentation_report.md).
|
||||
|
||||
## License Information
|
||||
|
||||
GNU General Public License v3.0 or later.
|
||||
|
||||
See [LICENCE](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text.
|
||||
|
||||
See [COPYING](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text.
|
||||
|
||||
4
bindep.txt
Normal file
4
bindep.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# This is a cross-platform list tracking distribution packages needed by tests;
|
||||
# see https://docs.openstack.org/infra/bindep/ for additional information.
|
||||
|
||||
rsync [platform:redhat]
|
||||
492
changelogs/changelog.yaml
Normal file
492
changelogs/changelog.yaml
Normal file
@@ -0,0 +1,492 @@
|
||||
ancestor: null
|
||||
releases:
|
||||
1.0.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Allow unsetting existing environment vars via environment by specifying a
|
||||
null value (https://github.com/ansible/ansible/pull/68236).
|
||||
- Mount - Handle remount with new options (https://github.com/ansible/ansible/issues/59460).
|
||||
- Profile_tasks - result was a odict_items which is not subscriptable, so the
|
||||
slicing was failing (https://github.com/ansible/ansible/issues/59059).
|
||||
- Revert "mount - Check if src exists before mounted (ansible/ansible#61752)".
|
||||
- Typecast results before use in profile_tasks callback (https://github.com/ansible/ansible/issues/69563).
|
||||
- authorized_keys - Added FIDO2 security keys (https://github.com/ansible-collections/ansible.posix/issues/17).
|
||||
- authorized_keys - fix inconsistent return value for check mode (https://github.com/ansible-collections/ansible.posix/issues/37)
|
||||
- json callback - Fix host result to task references in the resultant JSON output
|
||||
for non-lockstep strategy plugins such as free (https://github.com/ansible/ansible/issues/65931)
|
||||
- mount - fix issues with ismount module_util pathing for Ansible 2.9 (fixes
|
||||
https://github.com/ansible-collections/ansible.posix/issues/21)
|
||||
- patch - fix FQCN usage for action plugin (https://github.com/ansible-collections/ansible.posix/issues/11)
|
||||
- selinux - add missing configuration keys for /etc/selinux/config (https://github.com/ansible-collections/ansible.posix/issues/23)
|
||||
- synchronize - fix FQCN usage for action plugin (https://github.com/ansible-collections/ansible.posix/issues/11)
|
||||
major_changes:
|
||||
- Bootstrap Collection (https://github.com/ansible-collections/ansible.posix/pull/1).
|
||||
minor_changes:
|
||||
- CI should use devel (https://github.com/ansible-collections/ansible.posix/pull/6).
|
||||
- Enable tests for at, patch and synchronize modules (https://github.com/ansible-collections/ansible.posix/pull/5).
|
||||
- Enabled tags in galaxy.yml (https://github.com/ansible-collections/ansible.posix/issues/18).
|
||||
- Migrate hacking/cgroup_perf_recap_graph.py to this collection, since the cgroup_perf_recap
|
||||
callback lives here.
|
||||
- Remove license key from galaxy.yml.
|
||||
- Remove sanity jobs from shippable (https://github.com/ansible-collections/ansible.posix/pull/43).
|
||||
- Removed ANSIBLE_METADATA from all the modules.
|
||||
- Revert "Enable at, patch and synchronize tests (https://github.com/ansible-collections/ansible.posix/pull/5)".
|
||||
- Update EXAMPLES section in modules to use FQCN.
|
||||
- Update README.md (https://github.com/ansible-collections/ansible.posix/pull/4/).
|
||||
fragments:
|
||||
- 11-action-plugins-use-fqcn.yml
|
||||
- 12_migrate_cgroup_perf_recap_graph.yml
|
||||
- 14_mount_option.yml
|
||||
- 15_profile_tasks.yml
|
||||
- 17_authorized_keys.yml
|
||||
- 19_enable_tags.yml
|
||||
- 21-mount-module_util-routing-issue.yml
|
||||
- 23-selinux-doesnt-create-missing-config-keys.yml
|
||||
- 25_ansible_metadata.yml
|
||||
- 26_profile_tasks_doc.yml
|
||||
- 27_update_examples.yml
|
||||
- 33_mount.yml
|
||||
- 35_disable_tests.yml
|
||||
- 37-authorized_keys-inconsistent-check-mode-values.yml
|
||||
- 39_remove_license.yml
|
||||
- 43_remove_shippable.yml
|
||||
- 4_update_readme.yml
|
||||
- 5_enable_tests.yml
|
||||
- 65931-json-callback-non-lockstep-output.yml
|
||||
- 6_test_devel.yml
|
||||
- 7_env.yml
|
||||
- initial_commit.yaml
|
||||
modules:
|
||||
- description: Set and retrieve file ACL information.
|
||||
name: acl
|
||||
namespace: ''
|
||||
- description: Schedule the execution of a command or script file via the at command
|
||||
name: at
|
||||
namespace: ''
|
||||
- description: Adds or removes an SSH authorized key
|
||||
name: authorized_key
|
||||
namespace: ''
|
||||
- description: Control active and configured mount points
|
||||
name: mount
|
||||
namespace: ''
|
||||
- description: Apply patch files using the GNU patch tool
|
||||
name: patch
|
||||
namespace: ''
|
||||
- description: Toggles SELinux booleans
|
||||
name: seboolean
|
||||
namespace: ''
|
||||
- description: Change policy and state of SELinux
|
||||
name: selinux
|
||||
namespace: ''
|
||||
- description: A wrapper around rsync to make common tasks in your playbooks quick
|
||||
and easy
|
||||
name: synchronize
|
||||
namespace: ''
|
||||
- description: Manage entries in sysctl.conf.
|
||||
name: sysctl
|
||||
namespace: ''
|
||||
release_date: '2020-06-19'
|
||||
1.1.0:
|
||||
changes:
|
||||
minor_changes:
|
||||
- firewalld - add firewalld module to ansible.posix collection
|
||||
fragments:
|
||||
- firewalld_migration.yml
|
||||
release_date: '2020-07-15'
|
||||
1.1.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fix synchronize to work with renamed docker and buildah connection plugins.
|
||||
minor_changes:
|
||||
- skippy - fixed the deprecation warning (by date) for skippy callback plugin
|
||||
fragments:
|
||||
- 74_synchronize_docker.yml
|
||||
- skippy_deprecation.yml
|
||||
release_date: '2020-09-02'
|
||||
1.2.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- at - add AIX support (https://github.com/ansible-collections/ansible.posix/pull/99).
|
||||
- synchronize - add ``community.docker.docker`` to the list of supported transports
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/132).
|
||||
- synchronize - do not prepend PWD when path is in form user@server:path or
|
||||
server:path (https://github.com/ansible-collections/ansible.posix/pull/118).
|
||||
- synchronize - fix for private_key overriding in synchronize module.
|
||||
- sysctl - do not persist sysctl when value is invalid (https://github.com/ansible-collections/ansible.posix/pull/101).
|
||||
minor_changes:
|
||||
- firewalld - bring the ``target`` feature back (https://github.com/ansible-collections/ansible.posix/issues/112).
|
||||
- fix sanity test for various modules.
|
||||
- synchronize - add the ``ssh_connection_multiplexing`` option to allow SSH
|
||||
connection multiplexing (https://github.com/ansible/ansible/issues/24365).
|
||||
release_summary: 'This is the minor release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
|
||||
have been added after the release of ``ansible.posix`` 1.1.0.'
|
||||
fragments:
|
||||
- 1.2.0.yml
|
||||
- 101-sysctl-dont-persist-when-invalid.yml
|
||||
- 118-synchronize_bugfix.yml
|
||||
- 120-synchronize_add_option.yml
|
||||
- 144_add_community_docker_connection_plugin_alias.yml
|
||||
- 82-private-key-override-fix.yml
|
||||
- 99-at_add_aix_support.yml
|
||||
- firewalld_zone_target.yml
|
||||
- misc_fix.yml
|
||||
release_date: '2021-03-08'
|
||||
1.3.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Synchronize module not recognizing remote ssh key (https://github.com/ansible-collections/ansible.posix/issues/24).
|
||||
- Synchronize not using quotes around arguments like --out-format (https://github.com/ansible-collections/ansible.posix/issues/190).
|
||||
- at - append line-separator to the end of the ``command`` (https://github.com/ansible-collections/ansible.posix/issues/169).
|
||||
- csh - define ``ECHO`` and ``COMMAND_SEP`` (https://github.com/ansible-collections/ansible.posix/issues/204).
|
||||
- firewalld - enable integration after migration (https://github.com/ansible-collections/ansible.posix/pull/239).
|
||||
- firewalld - ensure idempotency with firewalld 0.9.3 (https://github.com/ansible-collections/ansible.posix/issues/179).
|
||||
- firewalld - fix setting zone target to ``%%REJECT%%`` (https://github.com/ansible-collections/ansible.posix/pull/215).
|
||||
- mount - Handle ``boot`` option on Solaris correctly (https://github.com/ansible-collections/ansible.posix/issues/184).
|
||||
- synchronize - add ``community.podman.podman`` to the list of supported connection
|
||||
plugins (https://github.com/ansible-community/molecule-podman/issues/45).
|
||||
- synchronize - complete podman support for synchronize module.
|
||||
- synchronize - properly quote rsync CLI parameters (https://github.com/ansible-collections/ansible.posix/pull/241).
|
||||
- synchronize - replace removed ``ansible_ssh_user`` by ``ansible_user`` everywhere;
|
||||
do the same for ``ansible_ssh_port`` and ``ansible_ssh_host`` (https://github.com/ansible-collections/ansible.posix/issues/60).
|
||||
- synchronize - use SSH args from SSH connection plugin (https://github.com/ansible-collections/ansible.posix/issues/222).
|
||||
- synchronize - use become_user when invoking rsync on remote with sudo (https://github.com/ansible-collections/ansible.posix/issues/186).
|
||||
- sysctl - modifying conditional check for docker to fix tests being skipped
|
||||
(https://github.com/ansible-collections/ansible.posix/pull/226).
|
||||
minor_changes:
|
||||
- acl - add new alias ``recurse`` for ``recursive`` parameter (https://github.com/ansible-collections/ansible.posix/issues/124).
|
||||
- added 2.11 branch to test matrix, added ignore-2.12.txt.
|
||||
- authorized_key - add ``no_log=False`` in ``argument_spec`` to clear false-positives
|
||||
of ``no-log-needed`` (https://github.com/ansible-collections/ansible.posix/pull/156).
|
||||
- authorized_key - add a list of valid key types (https://github.com/ansible-collections/ansible.posix/issues/134).
|
||||
- mount - Change behavior of ``boot`` option to set ``noauto`` on BSD nodes
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/28).
|
||||
- mount - Change behavior of ``boot`` option to set ``noauto`` on Linux nodes
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/28).
|
||||
- mount - add ``no_log=False`` in ``argument_spec`` to clear false-positives
|
||||
of ``no-log-needed`` (https://github.com/ansible-collections/ansible.posix/pull/156).
|
||||
- mount - returns ``backup_file`` value when a backup fstab is created.
|
||||
- synchronize - add ``delay_updates`` option (https://github.com/ansible-collections/ansible.posix/issues/157).
|
||||
- synchronize - fix typo (https://github.com/ansible-collections/ansible.posix/pull/198).
|
||||
release_summary: 'This is the minor release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
|
||||
have been added after the release of ``ansible.posix`` 1.2.0.'
|
||||
fragments:
|
||||
- 1.3.0.yml
|
||||
- 124_acl.yml
|
||||
- 126_mount_not_returning_backup_file.yml
|
||||
- 134_authorized_key.yml
|
||||
- 156-fix_no-log-needed_false_positives.yml
|
||||
- 159-fix-60-deprecated-ansible_ssh_user.yml
|
||||
- 167-synchronize-add_delay_option.yml
|
||||
- 169_add_lineseparator_to_command.yml
|
||||
- 175_synchronize.yml
|
||||
- 179_firewalld.yml
|
||||
- 181-update_codecov_sh_url.yml
|
||||
- 185_mount_at_boot.yml
|
||||
- 187-fix-synchronize-become-user.yml
|
||||
- 193_firewalld.yml
|
||||
- 196_boot_opt_for_linux.yml
|
||||
- 203_boot_opt_for_bsd.yml
|
||||
- 204_csh_shell.yml
|
||||
- 207-mount_tests.yml
|
||||
- 213_quote_cmd_args.yml
|
||||
- 214-add_firewalld_info_module.yml
|
||||
- 215_fix_REJECT_target_name.yml
|
||||
- 217-restructure_authrized_key_test.yml
|
||||
- 222_synchronize.yml
|
||||
- 226_sysctl_fix_integration_test.yml
|
||||
- 229_add_podman_connection_plugin_to_synchronize.yml
|
||||
- 230_complete_podman_support_for_synchronize.yml
|
||||
- 233-fix-wrong-firewalld-version-info.yml
|
||||
- 241-synchronize-shell-quoting.yml
|
||||
- firewalld_integ_test.yml
|
||||
- firewalld_test.yml
|
||||
- sanity_test_ignore_file.yml
|
||||
- synchronize.yml
|
||||
- test_matrix.yml
|
||||
release_date: '2021-08-11'
|
||||
1.4.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fix for whitespace in source full path causing error ```code 23) at main.c(1330)
|
||||
[sender=3.2.3]``` (https://github.com/ansible-collections/ansible.posix/pull/278)
|
||||
- Include ``PSF-license.txt`` file for ``plugins/module_utils/_version.py``.
|
||||
- Use vendored version of ``distutils.version`` instead of the deprecated Python
|
||||
standard library to address PEP 632 (https://github.com/ansible-collections/ansible.posix/issues/303).
|
||||
- firewalld - Correct usage of queryForwardPort (https://github.com/ansible-collections/ansible.posix/issues/247).
|
||||
- firewalld - Refine the handling of exclusive options (https://github.com/ansible-collections/ansible.posix/issues/255).
|
||||
- mount - add a newline at the end of line in ``fstab`` (https://github.com/ansible-collections/ansible.posix/issues/210).
|
||||
- profile_tasks - Correctly calculate task execution time with serial execution
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/83).
|
||||
- seboolean - add ``python3-libsemanage`` package dependency for RHEL8+ systems.
|
||||
minor_changes:
|
||||
- firewalld - Show warning message that variable type of ``masquerade`` and
|
||||
``icmp_block_inversion`` will be changed from ``str`` to ``boolean`` in the
|
||||
future release (https://github.com/ansible-collections/ansible.posix/pull/254).
|
||||
- selinux - optionally update kernel boot params when disabling/re-enabling
|
||||
SELinux (https://github.com/ansible-collections/ansible.posix/pull/142).
|
||||
release_summary: 'This is the minor release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules in this collection that
|
||||
|
||||
have been added after the release of ``ansible.posix`` 1.3.0.'
|
||||
fragments:
|
||||
- 1.4.0.yml
|
||||
- 211_fstab_append_newline.yml
|
||||
- 247_firewalld.yml
|
||||
- 254_variable_warnings.yml
|
||||
- 255_authorized_key_url.yml
|
||||
- 263_profile_tasks_with_serial.yml
|
||||
- 272-copy_ignore_txt.yml
|
||||
- 277_fix_integration_test_on_devel.yml
|
||||
- 282_fix_unit_test_for_synchronize.yml
|
||||
- 287_firewalld_requirements.yml
|
||||
- 288_mounts_options.yml
|
||||
- 297_firewalld_exclusive_options_handling.yml
|
||||
- 299_seboolean_python3.yml
|
||||
- 302_shippable_exit_code.yml
|
||||
- 304_pep632.yml
|
||||
- 346_copy_ignore_txt_for_devel.yml
|
||||
- 347_add_branch_213.yml
|
||||
- 349_follow_new_guidelines.yml
|
||||
- 353_ci_fix_unittest_for_synchronize.yml
|
||||
- disable_selinux_via_kernel_cmdline.yml
|
||||
- psf-license.yml
|
||||
- sanity_fixes.yml
|
||||
- shell_escape_full_path_for_rsync.yml
|
||||
release_date: '2022-05-23'
|
||||
1.5.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Removed contentious terminology to match reference documentation in profile_tasks.
|
||||
- firewall - Fixed to output a more complete missing library message.
|
||||
- synchronize - Fixed hosts involved in rsync require the same password
|
||||
minor_changes:
|
||||
- mount - Add ``absent_from_fstab`` state (https://github.com/ansible-collections/ansible.posix/pull/166).
|
||||
- mount - Add ``ephemeral`` value for the ``state`` parameter, that allows to
|
||||
mount a filesystem without altering the ``fstab`` file (https://github.com/ansible-collections/ansible.posix/pull/267).
|
||||
- r4e_rpm_ostree - new module for validating package state on RHEL for Edge
|
||||
- rhel_facts - new facts module to handle RHEL specific facts
|
||||
- rhel_rpm_ostree - new module to handle RHEL rpm-ostree specific package management
|
||||
functionality
|
||||
- rpm_ostree_upgrade - new module to automate rpm-ostree upgrades
|
||||
- rpm_ostree_upgrade - new module to manage upgrades for rpm-ostree based systems
|
||||
fragments:
|
||||
- 166_mount_absent_fstab.yml
|
||||
- 267_mount_ephemeral.yml
|
||||
- 366_update_version_number_for_galaxy.yml
|
||||
- 371_refactoring_ci_process_202206.yml
|
||||
- 373_firewall_fix_missing_library_message.yml
|
||||
- 375_update_azp_container.yml
|
||||
- 380_update_usage_profile_tasks.yml
|
||||
- 386_follow_ci_testing_rules.yml
|
||||
- 389_ci_add_stable_214.yml
|
||||
- 390_hosts_involved_same_password.yml
|
||||
- 393-rpm-ostree.yml
|
||||
- 393_rhel_for_edge.yml
|
||||
- 400-fix-boolean-values-in-docs.yml
|
||||
- 401_document_module_default_values.yml
|
||||
- 407_fix_firewalld_port_test.yml
|
||||
- 409_update_azp_matrix.yml
|
||||
release_date: '2023-01-20'
|
||||
1.5.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fixed a bug where firewalld module fails to create/remove zones when the daemon
|
||||
is stopped
|
||||
- rhel_facts - Call exit_json with all keyword arguments
|
||||
minor_changes:
|
||||
- Add jsonl callback plugin to ansible.posix collection
|
||||
- firewalld - add `protocol` parameter
|
||||
release_summary: 'This is the minor release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules and plugins
|
||||
|
||||
in this collection that have been added after the release of
|
||||
|
||||
``ansible.posix`` 1.5.1.'
|
||||
fragments:
|
||||
- 1.5.2.yml
|
||||
- 399_firewalld_create_remove_zone_when_offline.yml
|
||||
- 413-synchronize-seealso.yml
|
||||
- 417-add-protocol-parameter.yml
|
||||
- 419-fix-patch-doc.yml
|
||||
- 425-support_test-sanity-docker-devel.yml
|
||||
- 426-firewalld_info-doc-update.yml
|
||||
- 434-fix-rhel_facts-exit_json.yml
|
||||
- 535-add-jsonl-callback.yml
|
||||
release_date: '2023-04-07'
|
||||
1.5.4:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Fix sysctl integration test failing on newer versions of core. Previously
|
||||
NoneType was allowable, now it fails to convert to a str type.
|
||||
- Support new sanity test for the ansible-core devel branch CI test (https://github.com/ansible-collections/ansible.posix/issues/446).
|
||||
- firewall - Fix issue where adding an interface to a zone would fail when the
|
||||
daemon is offline
|
||||
- firewall - Fix issue where opening a specific port resulted in opening the
|
||||
whole protocol of the specified port
|
||||
- firewalld - Consider value of masquerade and icmp_block_inversion parameters
|
||||
when a boolean like value is passed
|
||||
minor_changes:
|
||||
- json and jsonl - Add the ``ANSIBLE_JSON_INDENT`` parameter
|
||||
- json and jsonl - Add the ``path`` attribute into the play and task output
|
||||
fragments:
|
||||
- 343-fix-profile_tasks-callback-documentation.yml
|
||||
- 402_firewall_fix_offline_interface_add.yml
|
||||
- 404_firewalld_masquerade_icmp_block_inversion_bugfixes.yml
|
||||
- 440-json-add-path-field.yaml
|
||||
- 441-json-add-indent-parameter.yaml
|
||||
- 445_ci_support_stable-215.yml
|
||||
- 448_support_new_sanity_test.yml
|
||||
- 451_firewall_fix_protocol_parameter.yml
|
||||
- 456_sysctl_fix_nonetype.yml
|
||||
release_date: '2023-05-10'
|
||||
1.6.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- Bugfix in the documentation regarding the path option for authorised_key(https://github.com/ansible-collections/ansible.posix/issues/483).
|
||||
- seboolean - make it work with disabled SELinux
|
||||
- synchronize - maintain proper formatting of the remote paths (https://github.com/ansible-collections/ansible.posix/pull/361).
|
||||
- sysctl - fix sysctl to work properly on symlinks (https://github.com/ansible-collections/ansible.posix/issues/111).
|
||||
major_changes:
|
||||
- Dropping support for Ansible 2.9, ansible-core 2.15 will be minimum required
|
||||
version for this release
|
||||
minor_changes:
|
||||
- Add summary_only parameter to profile_roles and profile_tasks callbacks.
|
||||
- firewalld - add functionality to set forwarding (https://github.com/ansible-collections/ansible.posix/pull/548).
|
||||
- firewalld - added offline flag implementation (https://github.com/ansible-collections/ansible.posix/pull/484)
|
||||
- firewalld - respawn module to use the system python interpreter when the ``firewall``
|
||||
python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- firewalld_info - Only warn about ignored zones, when there are zones ignored.
|
||||
- firewalld_info - respawn module to use the system python interpreter when
|
||||
the ``firewall`` python module is not available for ``ansible_python_interpreter``
|
||||
(https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- mount - add no_log option for opts parameter (https://github.com/ansible-collections/ansible.posix/pull/563).
|
||||
- seboolean - respawn module to use the system python interpreter when the ``selinux``
|
||||
python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
- selinux - respawn module to use the system python interpreter when the ``selinux``
|
||||
python module is not available for ``ansible_python_interpreter`` (https://github.com/ansible-collections/ansible.posix/pull/460).
|
||||
release_summary: 'This is the minor release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules and plugins
|
||||
|
||||
in this collection that have been added after the release of
|
||||
|
||||
``ansible.posix`` 1.5.4.'
|
||||
removed_features:
|
||||
- skippy - Remove skippy pluglin as it is no longer supported(https://github.com/ansible-collections/ansible.posix/issues/350).
|
||||
fragments:
|
||||
- 1.6.0.yml
|
||||
- 206_fix_sysctl_to_work_on_symlinks.yml
|
||||
- 333_doc_absent_precision.yml
|
||||
- 361_maintain_proper_formating_remote_paths.yml
|
||||
- 421-remove-deprecation-warning.yml
|
||||
- 460-respawn.yaml
|
||||
- 466-tests.yml
|
||||
- 477_ci_update.yml
|
||||
- 484-firewalld-offline.yml
|
||||
- 487_ci_update.yml
|
||||
- 490_doc_authorized_key_path.yml
|
||||
- 496_seboolean-make-it-wrk-with-SELinux-disabled.yaml
|
||||
- 504-firewalld_info-warning.yaml
|
||||
- 508_ci_update.yml
|
||||
- 510_ci_update.yml
|
||||
- 511_profile-callbacks-add-summary-only-parameter.yml
|
||||
- 548_add_foward.yml
|
||||
- 556_remove_skippy_callback.yml
|
||||
- 562_update_core_version.yml
|
||||
- 563_add_no_log_option.yml
|
||||
- dropping-ansible29.yml
|
||||
- test-reqs.yml
|
||||
release_date: '2024-09-11'
|
||||
1.6.1:
|
||||
changes:
|
||||
bugfixes:
|
||||
- acl - Fixed to set ACLs on paths mounted with NFS version 4 correctly (https://github.com/ansible-collections/ansible.posix/issues/240).
|
||||
- mount - Handle ``boot`` option on Linux, NetBSD and OpenBSD correctly (https://github.com/ansible-collections/ansible.posix/issues/364).
|
||||
- skippy - Revert removal of skippy plugin. It will be removed in version 2.0.0
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/573).
|
||||
release_summary: 'This is the bugfix release of the stable version ``ansible.posix``
|
||||
collection.
|
||||
|
||||
This changelog contains all changes to the modules and plugins
|
||||
|
||||
in this collection that have been added after the release of
|
||||
|
||||
``ansible.posix`` 1.6.1.'
|
||||
fragments:
|
||||
- 1.6.1.yml
|
||||
- 365-boot-linux.yml
|
||||
- 566_bump_version_161.yml
|
||||
- 567_remove_version_added.yml
|
||||
- 570_nfs4_acl.yml
|
||||
- 571_ci_bump_core_version.yml
|
||||
- 572_revert_removal_of_skippy.yml
|
||||
release_date: '2024-10-11'
|
||||
1.6.2:
|
||||
changes:
|
||||
bugfixes:
|
||||
- backport - Drop ansible-core 2.14 and set 2.15 minimum version (https://github.com/ansible-collections/ansible.posix/issues/578).
|
||||
release_summary: 'This is the bugfix release of the stable version ``ansible.posix``
|
||||
collection.
|
||||
|
||||
This changelog contains all changes to the modules and plugins
|
||||
|
||||
in this collection that have been added after the release of
|
||||
|
||||
``ansible.posix`` 1.6.1.'
|
||||
fragments:
|
||||
- 1.6.2.yml
|
||||
- 580_drop_ansible214.yml
|
||||
release_date: '2024-10-22'
|
||||
2.0.0:
|
||||
changes:
|
||||
breaking_changes:
|
||||
- firewalld - Changed the type of forward and masquerade options from str to
|
||||
bool (https://github.com/ansible-collections/ansible.posix/issues/582).
|
||||
- firewalld - Changed the type of icmp_block_inversion option from str to bool
|
||||
(https://github.com/ansible-collections/ansible.posix/issues/586).
|
||||
bugfixes:
|
||||
- acl - Fixed to set ACLs on paths mounted with NFS version 4 correctly (https://github.com/ansible-collections/ansible.posix/issues/240).
|
||||
- mount - Handle ``boot`` option on Linux, NetBSD and OpenBSD correctly (https://github.com/ansible-collections/ansible.posix/issues/364).
|
||||
- mount - If a comment is appended to a fstab entry, state present creates a
|
||||
double-entry (https://github.com/ansible-collections/ansible.posix/issues/595).
|
||||
minor_changes:
|
||||
- authorized_keys - allow using absolute path to a file as a SSH key(s) source
|
||||
(https://github.com/ansible-collections/ansible.posix/pull/568)
|
||||
- callback plugins - Add recap information to timer, profile_roles and profile_tasks
|
||||
callback outputs (https://github.com/ansible-collections/ansible.posix/pull/387).
|
||||
release_summary: 'This is the major release of the ``ansible.posix`` collection.
|
||||
|
||||
This changelog contains all changes to the modules and plugins
|
||||
|
||||
in this collection that have been added after the release of
|
||||
|
||||
``ansible.posix`` 1.6.2'
|
||||
removed_features:
|
||||
- skippy - Remove skippy pluglin as it is no longer supported(https://github.com/ansible-collections/ansible.posix/issues/350).
|
||||
fragments:
|
||||
- 2.0.0.yml
|
||||
- 365-boot-linux.yml
|
||||
- 387_callback_output_header.yml
|
||||
- 556_remove_skippy_callback.yml
|
||||
- 566_bump_version_161.yml
|
||||
- 567_remove_version_added.yml
|
||||
- 568_update_authorized_key.yml
|
||||
- 570_nfs4_acl.yml
|
||||
- 571_ci_bump_core_version.yml
|
||||
- 576_bump_version_2.yml
|
||||
- 581_ci_selinux.yml
|
||||
- 584_firewalld_opt_type.yml
|
||||
- 587_update_README.yml
|
||||
- 588_ci_enable_devel.yml
|
||||
- 593_replace_freebsd_version.yml
|
||||
- 597_remove_fstab_comment_on_updating.yml
|
||||
- 598_icmp_block_inversion.yml
|
||||
release_date: '2024-12-04'
|
||||
29
changelogs/config.yaml
Normal file
29
changelogs/config.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
changelog_filename_template: ../CHANGELOG.rst
|
||||
changelog_filename_version_depth: 0
|
||||
changes_file: changelog.yaml
|
||||
changes_format: combined
|
||||
keep_fragments: false
|
||||
mention_ancestor: true
|
||||
new_plugins_after_name: removed_features
|
||||
notesdir: fragments
|
||||
prelude_section_name: release_summary
|
||||
prelude_section_title: Release Summary
|
||||
sections:
|
||||
- - major_changes
|
||||
- Major Changes
|
||||
- - minor_changes
|
||||
- Minor Changes
|
||||
- - breaking_changes
|
||||
- Breaking Changes / Porting Guide
|
||||
- - deprecated_features
|
||||
- Deprecated Features
|
||||
- - removed_features
|
||||
- Removed Features (previously deprecated)
|
||||
- - security_fixes
|
||||
- Security Fixes
|
||||
- - bugfixes
|
||||
- Bugfixes
|
||||
- - known_issues
|
||||
- Known Issues
|
||||
title: ansible.posix
|
||||
trivial_section_name: trivial
|
||||
0
changelogs/fragments/.keep
Normal file
0
changelogs/fragments/.keep
Normal file
2
changelogs/fragments/603_bump_version_3.yml
Normal file
2
changelogs/fragments/603_bump_version_3.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
trivial:
|
||||
- Bump version to 3.0.0 for the next release (https://github.com/ansible-collections/ansible.posix/issues/603).
|
||||
2
changelogs/fragments/618_ci_remove_ubuntu2004.yml
Normal file
2
changelogs/fragments/618_ci_remove_ubuntu2004.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
trivial:
|
||||
- Remove ubuntu20.04 from CI tests (https://github.com/ansible-collections/ansible.posix/issues/612).
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
minor_changes:
|
||||
- profile_tasks - Add option to provide a different date/time format (https://github.com/ansible-collections/ansible.posix/issues/279).
|
||||
6
changelogs/fragments/631_fixes_module_path.yml
Normal file
6
changelogs/fragments/631_fixes_module_path.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
bugfixes:
|
||||
- ansible.posix.cgroup_perf_recap - fixes json module load path (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||
trivial:
|
||||
- ansible.posix.seboolean - remove unnecessary condition from seboolean integration tests (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||
- ansible.posix.selinux - optimize conditions for selinux integration tests (https://github.com/ansible-collections/ansible.posix/issues/630).
|
||||
2
changelogs/fragments/642_ci_add_rhel10.yml
Normal file
2
changelogs/fragments/642_ci_add_rhel10.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
trivial:
|
||||
- Add Red Hat Enterprise Linux 10.0 to the CI matrix (https://github.com/ansible-collections/ansible.posix/issues/642).
|
||||
2
changelogs/fragments/650-profile_tasks_roles.yml
Normal file
2
changelogs/fragments/650-profile_tasks_roles.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
minor_changes:
|
||||
- "profile_tasks and profile_roles callback plugins - avoid deleted/deprecated callback functions, instead use modern interface that was introduced a longer time ago (https://github.com/ansible-collections/ansible.posix/issues/650)."
|
||||
3
changelogs/fragments/654_ci_bump_core_version.yml
Normal file
3
changelogs/fragments/654_ci_bump_core_version.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
trivial:
|
||||
- Bump ansible-core version to 2.20 of devel branch and add 2.19 to CI
|
||||
2
changelogs/fragments/660_ci_azp_syntax.yml
Normal file
2
changelogs/fragments/660_ci_azp_syntax.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
trivial:
|
||||
- AZP - fixed syntax error in CI test.
|
||||
3
changelogs/fragments/665_update_readme_20250728.yml
Normal file
3
changelogs/fragments/665_update_readme_20250728.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
trivial:
|
||||
- README - Update README to reflect Ansible Core 2.19 release.
|
||||
3
changelogs/fragments/666_azp_update_20250728.yml
Normal file
3
changelogs/fragments/666_azp_update_20250728.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
trivial:
|
||||
- AZP - Update AZP matrix to follow ansible-test changes.
|
||||
2
changelogs/fragments/673_update_ci_20250805.yml
Normal file
2
changelogs/fragments/673_update_ci_20250805.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
trivial:
|
||||
- Update AZP CI matrix (https://github.com/ansible-collections/ansible.posix/issues/673).
|
||||
2
codecov.yml
Normal file
2
codecov.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
comment: false
|
||||
@@ -1,14 +1,15 @@
|
||||
---
|
||||
namespace: ansible
|
||||
name: posix
|
||||
version: 0.1.1
|
||||
version: 3.0.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (github.com/ansible)
|
||||
description: null
|
||||
description: Ansible Collection targeting POSIX and POSIX-ish platforms.
|
||||
license_file: COPYING
|
||||
tags: [posix, networking, shell, unix]
|
||||
dependencies: {}
|
||||
repository: https://github.com/ansible-collections/ansible.posix
|
||||
documentation: https://github.com/ansible-collections/ansible.posix/tree/master/docs
|
||||
documentation: https://docs.ansible.com/ansible/latest/collections/ansible/posix/
|
||||
homepage: https://github.com/ansible-collections/ansible.posix
|
||||
issues: https://github.com/ansible-collections/ansible.posix
|
||||
|
||||
2
meta/runtime.yml
Normal file
2
meta/runtime.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
requires_ansible: ">=2.16.0"
|
||||
@@ -64,7 +64,7 @@ class ActionModule(ActionBase):
|
||||
)
|
||||
)
|
||||
|
||||
result.update(self._execute_module('patch', module_args=new_module_args, task_vars=task_vars))
|
||||
result.update(self._execute_module('ansible.posix.patch', module_args=new_module_args, task_vars=task_vars))
|
||||
except AnsibleAction as e:
|
||||
result.update(e.result)
|
||||
finally:
|
||||
|
||||
@@ -21,6 +21,7 @@ import os.path
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.common._collections_compat import MutableSequence
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
@@ -28,12 +29,23 @@ from ansible.plugins.action import ActionBase
|
||||
from ansible.plugins.loader import connection_loader
|
||||
|
||||
|
||||
DOCKER = ['docker', 'community.general.docker', 'community.docker.docker']
|
||||
PODMAN = ['podman', 'ansible.builtin.podman', 'containers.podman.podman']
|
||||
BUILDAH = ['buildah', 'containers.podman.buildah']
|
||||
|
||||
|
||||
class ActionModule(ActionBase):
|
||||
|
||||
def _get_absolute_path(self, path):
|
||||
original_path = path
|
||||
|
||||
if path.startswith('rsync://'):
|
||||
#
|
||||
# Check if we have a local relative path and do not process
|
||||
# * remote paths (some.server.domain:/some/remote/path/...)
|
||||
# * URLs (rsync://...)
|
||||
# * local absolute paths (/some/local/path/...)
|
||||
#
|
||||
if ':' in path or path.startswith('/'):
|
||||
return path
|
||||
|
||||
if self._task._role is not None:
|
||||
@@ -60,21 +72,26 @@ class ActionModule(ActionBase):
|
||||
return path
|
||||
|
||||
# If using docker or buildah, do not add user information
|
||||
if self._remote_transport not in ['docker', 'buildah'] and user:
|
||||
if self._remote_transport not in DOCKER + PODMAN + BUILDAH and user:
|
||||
user_prefix = '%s@' % (user, )
|
||||
|
||||
if self._host_is_ipv6_address(host):
|
||||
return '[%s%s]:%s' % (user_prefix, host, path)
|
||||
else:
|
||||
|
||||
# preserve formatting of remote paths if host or user@host is explicitly defined in the path
|
||||
if ':' not in path:
|
||||
return '%s%s:%s' % (user_prefix, host, path)
|
||||
elif '@' not in path:
|
||||
return '%s%s' % (user_prefix, path)
|
||||
else:
|
||||
return path
|
||||
|
||||
def _process_origin(self, host, path, user):
|
||||
|
||||
if host not in C.LOCALHOST:
|
||||
return self._format_rsync_rsh_target(host, path, user)
|
||||
|
||||
if ':' not in path and not path.startswith('/'):
|
||||
path = self._get_absolute_path(path=path)
|
||||
path = self._get_absolute_path(path=path)
|
||||
return path
|
||||
|
||||
def _process_remote(self, task_args, host, path, user, port_matches_localhost_port):
|
||||
@@ -103,8 +120,7 @@ class ActionModule(ActionBase):
|
||||
task_args['_substitute_controller'] = True
|
||||
return self._format_rsync_rsh_target(host, path, user)
|
||||
|
||||
if ':' not in path and not path.startswith('/'):
|
||||
path = self._get_absolute_path(path=path)
|
||||
path = self._get_absolute_path(path=path)
|
||||
return path
|
||||
|
||||
def _override_module_replaced_vars(self, task_vars):
|
||||
@@ -168,12 +184,25 @@ class ActionModule(ActionBase):
|
||||
|
||||
# Store remote connection type
|
||||
self._remote_transport = self._connection.transport
|
||||
use_ssh_args = _tmp_args.pop('use_ssh_args', False)
|
||||
|
||||
if use_ssh_args and self._connection.transport == 'ssh':
|
||||
ssh_args = [
|
||||
self._connection.get_option('ssh_args'),
|
||||
self._connection.get_option('ssh_common_args'),
|
||||
self._connection.get_option('ssh_extra_args'),
|
||||
]
|
||||
_tmp_args['_ssh_args'] = ' '.join([a for a in ssh_args if a])
|
||||
|
||||
# Handle docker connection options
|
||||
if self._remote_transport == 'docker':
|
||||
if self._remote_transport in DOCKER:
|
||||
self._docker_cmd = self._connection.docker_cmd
|
||||
if self._play_context.docker_extra_args:
|
||||
self._docker_cmd = "%s %s" % (self._docker_cmd, self._play_context.docker_extra_args)
|
||||
elif self._remote_transport in PODMAN:
|
||||
self._docker_cmd = self._connection._options['podman_executable']
|
||||
if self._connection._options.get('podman_extra_args'):
|
||||
self._docker_cmd = "%s %s" % (self._docker_cmd, self._connection._options['podman_extra_args'])
|
||||
|
||||
# self._connection accounts for delegate_to so
|
||||
# remote_transport is the transport ansible thought it would need
|
||||
@@ -191,8 +220,8 @@ class ActionModule(ActionBase):
|
||||
|
||||
# ssh paramiko docker buildah and local are fully supported transports. Anything
|
||||
# else only works with delegate_to
|
||||
if delegate_to is None and self._connection.transport not in \
|
||||
('ssh', 'paramiko', 'local', 'docker', 'buildah'):
|
||||
if delegate_to is None and self._connection.transport not in [
|
||||
'ssh', 'paramiko', 'local'] + DOCKER + PODMAN + BUILDAH:
|
||||
result['failed'] = True
|
||||
result['msg'] = (
|
||||
"synchronize uses rsync to function. rsync needs to connect to the remote "
|
||||
@@ -201,11 +230,8 @@ class ActionModule(ActionBase):
|
||||
"so it cannot work." % self._connection.transport)
|
||||
return result
|
||||
|
||||
use_ssh_args = _tmp_args.pop('use_ssh_args', None)
|
||||
|
||||
# Parameter name needed by the ansible module
|
||||
_tmp_args['_local_rsync_path'] = task_vars.get('ansible_rsync_path') or 'rsync'
|
||||
_tmp_args['_local_rsync_password'] = task_vars.get('ansible_ssh_pass') or task_vars.get('ansible_password')
|
||||
|
||||
# rsync thinks that one end of the connection is localhost and the
|
||||
# other is the host we're running the task for (Note: We use
|
||||
@@ -215,14 +241,10 @@ class ActionModule(ActionBase):
|
||||
src_host = '127.0.0.1'
|
||||
inventory_hostname = task_vars.get('inventory_hostname')
|
||||
dest_host_inventory_vars = task_vars['hostvars'].get(inventory_hostname)
|
||||
try:
|
||||
dest_host = dest_host_inventory_vars['ansible_host']
|
||||
except KeyError:
|
||||
dest_host = dest_host_inventory_vars.get('ansible_ssh_host', inventory_hostname)
|
||||
dest_host = dest_host_inventory_vars.get('ansible_host', inventory_hostname)
|
||||
|
||||
dest_host_ids = [hostid for hostid in (dest_host_inventory_vars.get('inventory_hostname'),
|
||||
dest_host_inventory_vars.get('ansible_host'),
|
||||
dest_host_inventory_vars.get('ansible_ssh_host'))
|
||||
dest_host_inventory_vars.get('ansible_host'))
|
||||
if hostid is not None]
|
||||
|
||||
localhost_ports = set()
|
||||
@@ -246,7 +268,7 @@ class ActionModule(ActionBase):
|
||||
dest_is_local = True
|
||||
|
||||
# CHECK FOR NON-DEFAULT SSH PORT
|
||||
inv_port = task_vars.get('ansible_ssh_port', None) or C.DEFAULT_REMOTE_PORT
|
||||
inv_port = task_vars.get('ansible_port', None) or C.DEFAULT_REMOTE_PORT
|
||||
if _tmp_args.get('dest_port', None) is None:
|
||||
if inv_port is not None:
|
||||
_tmp_args['dest_port'] = inv_port
|
||||
@@ -269,9 +291,6 @@ class ActionModule(ActionBase):
|
||||
# told (via delegate_to) that a different host is the source of the
|
||||
# rsync
|
||||
if not use_delegate and remote_transport:
|
||||
# Create a connection to localhost to run rsync on
|
||||
new_stdin = self._connection._new_stdin
|
||||
|
||||
# Unlike port, there can be only one shell
|
||||
localhost_shell = None
|
||||
for host in C.LOCALHOST:
|
||||
@@ -300,7 +319,11 @@ class ActionModule(ActionBase):
|
||||
localhost_executable = C.DEFAULT_EXECUTABLE
|
||||
self._play_context.executable = localhost_executable
|
||||
|
||||
new_connection = connection_loader.get('local', self._play_context, new_stdin)
|
||||
try:
|
||||
new_connection = connection_loader.get('local', self._play_context)
|
||||
except TypeError:
|
||||
# Needed for ansible-core < 2.15
|
||||
new_connection = connection_loader.get('local', self._play_context, self._connection._new_stdin)
|
||||
self._connection = new_connection
|
||||
# Override _remote_is_local as an instance attribute specifically for the synchronize use case
|
||||
# ensuring we set local tmpdir correctly
|
||||
@@ -317,26 +340,27 @@ class ActionModule(ActionBase):
|
||||
if src is None or dest is None:
|
||||
return dict(failed=True, msg="synchronize requires both src and dest parameters are set")
|
||||
|
||||
# Determine if we need a user@
|
||||
# Determine if we need a user@ and a password
|
||||
user = None
|
||||
password = task_vars.get('ansible_ssh_pass', None) or task_vars.get('ansible_password', None)
|
||||
if not dest_is_local:
|
||||
# Src and dest rsync "path" handling
|
||||
if boolean(_tmp_args.get('set_remote_user', 'yes'), strict=False):
|
||||
if use_delegate:
|
||||
user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_ssh_user', None)
|
||||
user = task_vars.get('ansible_delegated_vars', dict()).get('ansible_user', None)
|
||||
if not user:
|
||||
user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user
|
||||
user = task_vars.get('ansible_user') or self._play_context.remote_user
|
||||
if not user:
|
||||
user = C.DEFAULT_REMOTE_USER
|
||||
|
||||
else:
|
||||
user = task_vars.get('ansible_ssh_user') or self._play_context.remote_user
|
||||
user = task_vars.get('ansible_user') or self._play_context.remote_user
|
||||
|
||||
if self._templar is not None:
|
||||
user = self._templar.template(user)
|
||||
|
||||
# Private key handling
|
||||
private_key = self._play_context.private_key_file
|
||||
|
||||
if private_key is not None:
|
||||
_tmp_args['private_key'] = private_key
|
||||
# Use the private_key parameter if passed else use context private_key_file
|
||||
_tmp_args['private_key'] = _tmp_args.get('private_key', self._play_context.private_key_file)
|
||||
|
||||
# use the mode to define src and dest's url
|
||||
if _tmp_args.get('mode', 'push') == 'pull':
|
||||
@@ -347,14 +371,17 @@ class ActionModule(ActionBase):
|
||||
# src is a local path, dest is a remote path: <user>@<host>
|
||||
src = self._process_origin(src_host, src, user)
|
||||
dest = self._process_remote(_tmp_args, dest_host, dest, user, inv_port in localhost_ports)
|
||||
|
||||
password = dest_host_inventory_vars.get('ansible_ssh_pass', None) or dest_host_inventory_vars.get('ansible_password', None)
|
||||
if self._templar is not None:
|
||||
password = self._templar.template(password)
|
||||
else:
|
||||
# Still need to munge paths (to account for roles) even if we aren't
|
||||
# copying files between hosts
|
||||
if not src.startswith('/'):
|
||||
src = self._get_absolute_path(path=src)
|
||||
if not dest.startswith('/'):
|
||||
dest = self._get_absolute_path(path=dest)
|
||||
src = self._get_absolute_path(path=src)
|
||||
dest = self._get_absolute_path(path=dest)
|
||||
|
||||
_tmp_args['_local_rsync_password'] = password
|
||||
_tmp_args['src'] = src
|
||||
_tmp_args['dest'] = dest
|
||||
|
||||
@@ -367,11 +394,14 @@ class ActionModule(ActionBase):
|
||||
if not dest_is_local:
|
||||
# don't escalate for docker. doing --rsync-path with docker exec fails
|
||||
# and we can switch directly to the user via docker arguments
|
||||
if self._play_context.become and not rsync_path and self._remote_transport != 'docker':
|
||||
if self._play_context.become and not rsync_path and self._remote_transport not in DOCKER + PODMAN:
|
||||
# If no rsync_path is set, become was originally set, and dest is
|
||||
# remote then add privilege escalation here.
|
||||
if self._play_context.become_method == 'sudo':
|
||||
rsync_path = 'sudo rsync'
|
||||
if self._play_context.become_user:
|
||||
rsync_path = 'sudo -u %s rsync' % self._play_context.become_user
|
||||
else:
|
||||
rsync_path = 'sudo rsync'
|
||||
# TODO: have to add in the rest of the become methods here
|
||||
|
||||
# We cannot use privilege escalation on the machine running the
|
||||
@@ -381,17 +411,9 @@ class ActionModule(ActionBase):
|
||||
|
||||
_tmp_args['rsync_path'] = rsync_path
|
||||
|
||||
if use_ssh_args:
|
||||
ssh_args = [
|
||||
getattr(self._play_context, 'ssh_args', ''),
|
||||
getattr(self._play_context, 'ssh_common_args', ''),
|
||||
getattr(self._play_context, 'ssh_extra_args', ''),
|
||||
]
|
||||
_tmp_args['ssh_args'] = ' '.join([a for a in ssh_args if a])
|
||||
|
||||
# If launching synchronize against docker container
|
||||
# use rsync_opts to support container to override rsh options
|
||||
if self._remote_transport in ['docker', 'buildah'] and not use_delegate:
|
||||
if self._remote_transport in DOCKER + BUILDAH + PODMAN and not use_delegate:
|
||||
# Replicate what we do in the module argumentspec handling for lists
|
||||
if not isinstance(_tmp_args.get('rsync_opts'), MutableSequence):
|
||||
tmp_rsync_opts = _tmp_args.get('rsync_opts', [])
|
||||
@@ -404,17 +426,17 @@ class ActionModule(ActionBase):
|
||||
if '--blocking-io' not in _tmp_args['rsync_opts']:
|
||||
_tmp_args['rsync_opts'].append('--blocking-io')
|
||||
|
||||
if self._remote_transport in ['docker']:
|
||||
if self._remote_transport in DOCKER + PODMAN:
|
||||
if become and self._play_context.become_user:
|
||||
_tmp_args['rsync_opts'].append("--rsh=%s exec -u %s -i" % (self._docker_cmd, self._play_context.become_user))
|
||||
_tmp_args['rsync_opts'].append('--rsh=' + shlex_quote('%s exec -u %s -i' % (self._docker_cmd, self._play_context.become_user)))
|
||||
elif user is not None:
|
||||
_tmp_args['rsync_opts'].append("--rsh=%s exec -u %s -i" % (self._docker_cmd, user))
|
||||
_tmp_args['rsync_opts'].append('--rsh=' + shlex_quote('%s exec -u %s -i' % (self._docker_cmd, user)))
|
||||
else:
|
||||
_tmp_args['rsync_opts'].append("--rsh=%s exec -i" % self._docker_cmd)
|
||||
elif self._remote_transport in ['buildah']:
|
||||
_tmp_args['rsync_opts'].append("--rsh=buildah run --")
|
||||
_tmp_args['rsync_opts'].append('--rsh=' + shlex_quote('%s exec -i' % self._docker_cmd))
|
||||
elif self._remote_transport in BUILDAH:
|
||||
_tmp_args['rsync_opts'].append('--rsh=' + shlex_quote('buildah run --'))
|
||||
|
||||
# run the module and store the result
|
||||
result.update(self._execute_module('synchronize', module_args=_tmp_args, task_vars=task_vars))
|
||||
result.update(self._execute_module('ansible.posix.synchronize', module_args=_tmp_args, task_vars=task_vars))
|
||||
|
||||
return result
|
||||
|
||||
@@ -11,8 +11,8 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: cgroup_perf_recap
|
||||
callback_type: aggregate
|
||||
name: cgroup_perf_recap
|
||||
type: aggregate
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
- cgroups
|
||||
@@ -132,6 +132,7 @@ DOCUMENTATION = '''
|
||||
|
||||
import csv
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
@@ -142,7 +143,7 @@ from functools import partial
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.six import with_metaclass
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder, json
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: debug
|
||||
name: debug
|
||||
type: stdout
|
||||
short_description: formatted stdout/stderr display
|
||||
description:
|
||||
|
||||
@@ -7,7 +7,7 @@ from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: json
|
||||
name: json
|
||||
short_description: Ansible screen output as JSON
|
||||
description:
|
||||
- This callback converts all events into JSON output to stdout
|
||||
@@ -25,6 +25,21 @@ DOCUMENTATION = '''
|
||||
- key: show_custom_stats
|
||||
section: defaults
|
||||
type: bool
|
||||
json_indent:
|
||||
name: Use indenting for the JSON output
|
||||
description: 'If specified, use this many spaces for indenting in the JSON output. If <= 0, write to a single line.'
|
||||
default: 4
|
||||
env:
|
||||
- name: ANSIBLE_JSON_INDENT
|
||||
ini:
|
||||
- key: json_indent
|
||||
section: defaults
|
||||
type: integer
|
||||
notes:
|
||||
- When using a strategy such as free, host_pinned, or a custom strategy, host results will
|
||||
be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate
|
||||
task objects indicated by duplicate task IDs at ``.plays[].tasks[].task.id``, each with an
|
||||
individual host result for the task.
|
||||
'''
|
||||
|
||||
import datetime
|
||||
@@ -33,10 +48,14 @@ import json
|
||||
from functools import partial
|
||||
|
||||
from ansible.inventory.host import Host
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
LOCKSTEP_CALLBACKS = frozenset(('linear', 'debug'))
|
||||
|
||||
|
||||
def current_time():
|
||||
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
||||
|
||||
@@ -49,12 +68,22 @@ class CallbackModule(CallbackBase):
|
||||
def __init__(self, display=None):
|
||||
super(CallbackModule, self).__init__(display)
|
||||
self.results = []
|
||||
self._task_map = {}
|
||||
self._is_lockstep = False
|
||||
|
||||
self.set_options()
|
||||
|
||||
self._json_indent = self.get_option('json_indent')
|
||||
if self._json_indent <= 0:
|
||||
self._json_indent = None
|
||||
|
||||
def _new_play(self, play):
|
||||
self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS
|
||||
return {
|
||||
'play': {
|
||||
'name': play.get_name(),
|
||||
'id': str(play._uuid),
|
||||
'id': to_text(play._uuid),
|
||||
'path': to_text(play.get_path()),
|
||||
'duration': {
|
||||
'start': current_time()
|
||||
}
|
||||
@@ -66,7 +95,8 @@ class CallbackModule(CallbackBase):
|
||||
return {
|
||||
'task': {
|
||||
'name': task.get_name(),
|
||||
'id': str(task._uuid),
|
||||
'id': to_text(task._uuid),
|
||||
'path': to_text(task.get_path()),
|
||||
'duration': {
|
||||
'start': current_time()
|
||||
}
|
||||
@@ -74,13 +104,32 @@ class CallbackModule(CallbackBase):
|
||||
'hosts': {}
|
||||
}
|
||||
|
||||
def _find_result_task(self, host, task):
|
||||
key = (host.get_name(), task._uuid)
|
||||
return self._task_map.get(
|
||||
key,
|
||||
self.results[-1]['tasks'][-1]
|
||||
)
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
self.results.append(self._new_play(play))
|
||||
|
||||
def v2_runner_on_start(self, host, task):
|
||||
if self._is_lockstep:
|
||||
return
|
||||
key = (host.get_name(), task._uuid)
|
||||
task_result = self._new_task(task)
|
||||
self._task_map[key] = task_result
|
||||
self.results[-1]['tasks'].append(task_result)
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
if not self._is_lockstep:
|
||||
return
|
||||
self.results[-1]['tasks'].append(self._new_task(task))
|
||||
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
if not self._is_lockstep:
|
||||
return
|
||||
self.results[-1]['tasks'].append(self._new_task(task))
|
||||
|
||||
def _convert_host_to_name(self, key):
|
||||
@@ -112,20 +161,28 @@ class CallbackModule(CallbackBase):
|
||||
'global_custom_stats': global_custom_stats,
|
||||
}
|
||||
|
||||
self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=4, sort_keys=True))
|
||||
self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=self._json_indent, sort_keys=True))
|
||||
|
||||
def _record_task_result(self, on_info, result, **kwargs):
|
||||
"""This function is used as a partial to add failed/skipped info in a single method"""
|
||||
host = result._host
|
||||
task = result._task
|
||||
task_result = result._result.copy()
|
||||
task_result.update(on_info)
|
||||
task_result['action'] = task.action
|
||||
self.results[-1]['tasks'][-1]['hosts'][host.name] = task_result
|
||||
|
||||
result_copy = result._result.copy()
|
||||
result_copy.update(on_info)
|
||||
result_copy['action'] = task.action
|
||||
|
||||
task_result = self._find_result_task(host, task)
|
||||
|
||||
task_result['hosts'][host.name] = result_copy
|
||||
end_time = current_time()
|
||||
self.results[-1]['tasks'][-1]['task']['duration']['end'] = end_time
|
||||
task_result['task']['duration']['end'] = end_time
|
||||
self.results[-1]['play']['duration']['end'] = end_time
|
||||
|
||||
if not self._is_lockstep:
|
||||
key = (host.get_name(), task._uuid)
|
||||
del self._task_map[key]
|
||||
|
||||
def __getattribute__(self, name):
|
||||
"""Return ``_record_task_result`` partial with a dict containing skipped/failed if necessary"""
|
||||
if name not in ('v2_runner_on_ok', 'v2_runner_on_failed', 'v2_runner_on_unreachable', 'v2_runner_on_skipped'):
|
||||
|
||||
214
plugins/callback/jsonl.py
Normal file
214
plugins/callback/jsonl.py
Normal file
@@ -0,0 +1,214 @@
|
||||
# (c) 2016, Matt Martz <matt@sivel.net>
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: jsonl
|
||||
short_description: Ansible screen output as JSONL (lines in json format)
|
||||
description:
|
||||
- This callback converts all events into JSON output to stdout
|
||||
- This callback in contrast with ansible.posix.json uses less memory, because it doesn't store results.
|
||||
type: stdout
|
||||
requirements:
|
||||
- Set as stdout in config
|
||||
options:
|
||||
show_custom_stats:
|
||||
name: Show custom stats
|
||||
description: 'This adds the custom stats set via the set_stats plugin to the play recap'
|
||||
default: False
|
||||
env:
|
||||
- name: ANSIBLE_SHOW_CUSTOM_STATS
|
||||
ini:
|
||||
- key: show_custom_stats
|
||||
section: defaults
|
||||
type: bool
|
||||
json_indent:
|
||||
name: Use indenting for the JSON output
|
||||
description: 'If specified, use this many spaces for indenting in the JSON output. If not specified or <= 0, write to a single line.'
|
||||
default: 0
|
||||
env:
|
||||
- name: ANSIBLE_JSON_INDENT
|
||||
ini:
|
||||
- key: json_indent
|
||||
section: defaults
|
||||
type: integer
|
||||
notes:
|
||||
- When using a strategy such as free, host_pinned, or a custom strategy, host results will
|
||||
be added to new task results in ``.plays[].tasks[]``. As such, there will exist duplicate
|
||||
task objects indicated by duplicate task IDs at ``.plays[].tasks[].task.id``, each with an
|
||||
individual host result for the task.
|
||||
'''
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import copy
|
||||
|
||||
from functools import partial
|
||||
|
||||
from ansible.inventory.host import Host
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
LOCKSTEP_CALLBACKS = frozenset(('linear', 'debug'))
|
||||
|
||||
|
||||
def current_time():
|
||||
return '%sZ' % datetime.datetime.utcnow().isoformat()
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'stdout'
|
||||
CALLBACK_NAME = 'ansible.posix.jsonl'
|
||||
|
||||
def __init__(self, display=None):
|
||||
super(CallbackModule, self).__init__(display)
|
||||
self.results = []
|
||||
self._task_map = {}
|
||||
self._is_lockstep = False
|
||||
|
||||
self.set_options()
|
||||
|
||||
self._json_indent = self.get_option('json_indent')
|
||||
if self._json_indent <= 0:
|
||||
self._json_indent = None
|
||||
|
||||
def _new_play(self, play):
|
||||
self._is_lockstep = play.strategy in LOCKSTEP_CALLBACKS
|
||||
return {
|
||||
'play': {
|
||||
'name': play.get_name(),
|
||||
'id': to_text(play._uuid),
|
||||
'path': to_text(play.get_path()),
|
||||
'duration': {
|
||||
'start': current_time()
|
||||
}
|
||||
},
|
||||
'tasks': []
|
||||
}
|
||||
|
||||
def _new_task(self, task):
|
||||
return {
|
||||
'task': {
|
||||
'name': task.get_name(),
|
||||
'id': to_text(task._uuid),
|
||||
'path': to_text(task.get_path()),
|
||||
'duration': {
|
||||
'start': current_time()
|
||||
}
|
||||
},
|
||||
'hosts': {}
|
||||
}
|
||||
|
||||
def _find_result_task(self, host, task):
|
||||
key = (host.get_name(), task._uuid)
|
||||
return self._task_map.get(
|
||||
key,
|
||||
self.results[-1]['tasks'][-1]
|
||||
)
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
play_result = self._new_play(play)
|
||||
self.results.append(play_result)
|
||||
self._write_event('v2_playbook_on_play_start', play_result)
|
||||
|
||||
def v2_runner_on_start(self, host, task):
|
||||
if self._is_lockstep:
|
||||
return
|
||||
key = (host.get_name(), task._uuid)
|
||||
task_result = self._new_task(task)
|
||||
self._task_map[key] = task_result
|
||||
self.results[-1]['tasks'].append(task_result)
|
||||
self._write_event('v2_runner_on_start', task_result)
|
||||
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
if not self._is_lockstep:
|
||||
return
|
||||
task_result = self._new_task(task)
|
||||
self.results[-1]['tasks'].append(task_result)
|
||||
self._write_event('v2_playbook_on_task_start', task_result)
|
||||
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
if not self._is_lockstep:
|
||||
return
|
||||
task_result = self._new_task(task)
|
||||
self.results[-1]['tasks'].append(task_result)
|
||||
self._write_event('v2_playbook_on_handler_task_start', task_result)
|
||||
|
||||
def _convert_host_to_name(self, key):
|
||||
if isinstance(key, (Host,)):
|
||||
return key.get_name()
|
||||
return key
|
||||
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
"""Display info about playbook statistics"""
|
||||
|
||||
hosts = sorted(stats.processed.keys())
|
||||
|
||||
summary = {}
|
||||
for h in hosts:
|
||||
s = stats.summarize(h)
|
||||
summary[h] = s
|
||||
|
||||
custom_stats = {}
|
||||
global_custom_stats = {}
|
||||
|
||||
if self.get_option('show_custom_stats') and stats.custom:
|
||||
custom_stats.update(dict((self._convert_host_to_name(k), v) for k, v in stats.custom.items()))
|
||||
global_custom_stats.update(custom_stats.pop('_run', {}))
|
||||
|
||||
output = {
|
||||
'stats': summary,
|
||||
'custom_stats': custom_stats,
|
||||
'global_custom_stats': global_custom_stats,
|
||||
}
|
||||
|
||||
self._write_event('v2_playbook_on_stats', output)
|
||||
|
||||
def _write_event(self, event_name, output):
|
||||
output['_event'] = event_name
|
||||
output['_timestamp'] = current_time()
|
||||
self._display.display(json.dumps(output, cls=AnsibleJSONEncoder, indent=self._json_indent, separators=',:', sort_keys=True))
|
||||
|
||||
def _record_task_result(self, event_name, on_info, result, **kwargs):
|
||||
"""This function is used as a partial to add failed/skipped info in a single method"""
|
||||
host = result._host
|
||||
task = result._task
|
||||
|
||||
result_copy = result._result.copy()
|
||||
result_copy.update(on_info)
|
||||
result_copy['action'] = task.action
|
||||
|
||||
task_result = self._find_result_task(host, task)
|
||||
|
||||
end_time = current_time()
|
||||
task_result['task']['duration']['end'] = end_time
|
||||
self.results[-1]['play']['duration']['end'] = end_time
|
||||
|
||||
task_result_copy = copy.deepcopy(task_result)
|
||||
task_result_copy['hosts'][host.name] = result_copy
|
||||
|
||||
if not self._is_lockstep:
|
||||
key = (host.get_name(), task._uuid)
|
||||
del self._task_map[key]
|
||||
|
||||
self._write_event(event_name, task_result_copy)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
"""Return ``_record_task_result`` partial with a dict containing skipped/failed if necessary"""
|
||||
if name not in ('v2_runner_on_ok', 'v2_runner_on_failed', 'v2_runner_on_unreachable', 'v2_runner_on_skipped'):
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
on = name.rsplit('_', 1)[1]
|
||||
|
||||
on_info = {}
|
||||
if on in ('failed', 'skipped'):
|
||||
on_info[on] = True
|
||||
|
||||
return partial(self._record_task_result, name, on_info)
|
||||
@@ -7,13 +7,26 @@ from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: profile_roles
|
||||
name: profile_roles
|
||||
type: aggregate
|
||||
short_description: adds timing information to roles
|
||||
description:
|
||||
- This callback module provides profiling for ansible roles.
|
||||
requirements:
|
||||
- whitelisting in configuration
|
||||
options:
|
||||
summary_only:
|
||||
description:
|
||||
- Only show summary, not individual task profiles.
|
||||
Especially usefull in combination with C(DISPLAY_SKIPPED_HOSTS=false) and/or C(ANSIBLE_DISPLAY_OK_HOSTS=false).
|
||||
type: bool
|
||||
default: False
|
||||
env:
|
||||
- name: PROFILE_ROLES_SUMMARY_ONLY
|
||||
ini:
|
||||
- section: callback_profile_roles
|
||||
key: summary_only
|
||||
version_added: 1.5.0
|
||||
'''
|
||||
|
||||
import collections
|
||||
@@ -76,13 +89,26 @@ class CallbackModule(CallbackBase):
|
||||
self.stats = collections.Counter()
|
||||
self.totals = collections.Counter()
|
||||
self.current = None
|
||||
|
||||
self.summary_only = None
|
||||
|
||||
super(CallbackModule, self).__init__()
|
||||
|
||||
def set_options(self, task_keys=None, var_options=None, direct=None):
|
||||
|
||||
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||
|
||||
self.summary_only = self.get_option('summary_only')
|
||||
|
||||
def _display_tasktime(self):
|
||||
if not self.summary_only:
|
||||
self._display.display(tasktime())
|
||||
|
||||
def _record_task(self, task):
|
||||
"""
|
||||
Logs the start of each task
|
||||
"""
|
||||
self._display.display(tasktime())
|
||||
self._display_tasktime()
|
||||
timestamp(self)
|
||||
|
||||
if task._role:
|
||||
@@ -98,10 +124,10 @@ class CallbackModule(CallbackBase):
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
self._record_task(task)
|
||||
|
||||
def playbook_on_setup(self):
|
||||
self._display.display(tasktime())
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
# Align summary report header with other callback plugin summary
|
||||
self._display.banner("ROLES RECAP")
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
self._display.display(tasktime())
|
||||
self._display.display(filled("", fchar="="))
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: profile_tasks
|
||||
name: profile_tasks
|
||||
type: aggregate
|
||||
short_description: adds time information to tasks
|
||||
description:
|
||||
- Ansible callback plugin for timing individual tasks and overall execution time.
|
||||
- "Mashup of 2 excellent original works: https://github.com/jlafon/ansible-profile,
|
||||
https://github.com/junaid18183/ansible_home/blob/master/ansible_plugins/callback_plugins/timestamp.py.old"
|
||||
- "Format: C(<task start timestamp> (<length of previous task>) <current elapsed playbook execution time>)"
|
||||
- "Format: C(<task start timestamp>) C(<length of previous task>) C(<current elapsed playbook execution time>)"
|
||||
- It also lists the top/bottom time consuming tasks in the summary (configurable)
|
||||
- Before 2.4 only the environment variables were available for configuration.
|
||||
requirements:
|
||||
- whitelisting in configuration - see examples section below for details.
|
||||
- enable in configuration - see examples section below for details.
|
||||
options:
|
||||
output_limit:
|
||||
description: Number of tasks to display in the summary
|
||||
@@ -40,13 +40,36 @@ DOCUMENTATION = '''
|
||||
ini:
|
||||
- section: callback_profile_tasks
|
||||
key: sort_order
|
||||
summary_only:
|
||||
description:
|
||||
- Only show summary, not individual task profiles.
|
||||
Especially usefull in combination with C(DISPLAY_SKIPPED_HOSTS=false) and/or C(ANSIBLE_DISPLAY_OK_HOSTS=false).
|
||||
type: bool
|
||||
default: False
|
||||
env:
|
||||
- name: PROFILE_TASKS_SUMMARY_ONLY
|
||||
ini:
|
||||
- section: callback_profile_tasks
|
||||
key: summary_only
|
||||
version_added: 1.5.0
|
||||
datetime_format:
|
||||
description:
|
||||
- Datetime format, as expected by the C(strftime) and C(strptime) methods.
|
||||
An C(iso8601) alias will be translated to C('%Y-%m-%dT%H:%M:%S.%f') if that datetime standard wants to be used.
|
||||
default: '%A %d %B %Y %H:%M:%S %z'
|
||||
env:
|
||||
- name: PROFILE_TASKS_DATETIME_FORMAT
|
||||
ini:
|
||||
- section: callback_profile_tasks
|
||||
key: datetime_format
|
||||
version_added: 3.0.0
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
example: >
|
||||
To enable, add this to your ansible.cfg file in the defaults block
|
||||
[defaults]
|
||||
callback_whitelist = ansible.posix.profile_tasks
|
||||
callbacks_enabled=ansible.posix.profile_tasks
|
||||
sample output: >
|
||||
#
|
||||
# TASK: [ensure messaging security group exists] ********************************
|
||||
@@ -60,14 +83,15 @@ sample output: >
|
||||
'''
|
||||
|
||||
import collections
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from ansible.module_utils.six.moves import reduce
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
|
||||
# define start time
|
||||
t0 = tn = time.time()
|
||||
dt0 = dtn = datetime.now().astimezone()
|
||||
|
||||
|
||||
def secondsToStr(t):
|
||||
@@ -92,16 +116,18 @@ def filled(msg, fchar="*"):
|
||||
|
||||
def timestamp(self):
|
||||
if self.current is not None:
|
||||
self.stats[self.current]['time'] = time.time() - self.stats[self.current]['time']
|
||||
elapsed = (datetime.now().astimezone() - self.stats[self.current]['started']).total_seconds()
|
||||
self.stats[self.current]['elapsed'] += elapsed
|
||||
|
||||
|
||||
def tasktime():
|
||||
global tn
|
||||
time_current = time.strftime('%A %d %B %Y %H:%M:%S %z')
|
||||
time_elapsed = secondsToStr(time.time() - tn)
|
||||
time_total_elapsed = secondsToStr(time.time() - t0)
|
||||
tn = time.time()
|
||||
return filled('%s (%s)%s%s' % (time_current, time_elapsed, ' ' * 7, time_total_elapsed))
|
||||
def tasktime(self):
|
||||
global dtn
|
||||
cdtn = datetime.now().astimezone()
|
||||
datetime_current = cdtn.strftime(self.datetime_format)
|
||||
time_elapsed = secondsToStr((cdtn - dtn).total_seconds())
|
||||
time_total_elapsed = secondsToStr((cdtn - dt0).total_seconds())
|
||||
dtn = cdtn
|
||||
return filled('%s (%s)%s%s' % (datetime_current, time_elapsed, ' ' * 7, time_total_elapsed))
|
||||
|
||||
|
||||
class CallbackModule(CallbackBase):
|
||||
@@ -119,7 +145,9 @@ class CallbackModule(CallbackBase):
|
||||
self.current = None
|
||||
|
||||
self.sort_order = None
|
||||
self.summary_only = None
|
||||
self.task_output_limit = None
|
||||
self.datetime_format = None
|
||||
|
||||
super(CallbackModule, self).__init__()
|
||||
|
||||
@@ -136,6 +164,8 @@ class CallbackModule(CallbackBase):
|
||||
elif self.sort_order == 'none':
|
||||
self.sort_order = None
|
||||
|
||||
self.summary_only = self.get_option('summary_only')
|
||||
|
||||
self.task_output_limit = self.get_option('output_limit')
|
||||
if self.task_output_limit is not None:
|
||||
if self.task_output_limit == 'all':
|
||||
@@ -143,16 +173,33 @@ class CallbackModule(CallbackBase):
|
||||
else:
|
||||
self.task_output_limit = int(self.task_output_limit)
|
||||
|
||||
self.datetime_format = self.get_option('datetime_format')
|
||||
if self.datetime_format is not None:
|
||||
if self.datetime_format == 'iso8601':
|
||||
self.datetime_format = '%Y-%m-%dT%H:%M:%S.%f'
|
||||
|
||||
def _display_tasktime(self):
|
||||
if not self.summary_only:
|
||||
self._display.display(tasktime(self))
|
||||
|
||||
def _record_task(self, task):
|
||||
"""
|
||||
Logs the start of each task
|
||||
"""
|
||||
self._display.display(tasktime())
|
||||
self._display_tasktime()
|
||||
timestamp(self)
|
||||
|
||||
# Record the start time of the current task
|
||||
# stats[TASK_UUID]:
|
||||
# started: Current task start time. This value will be updated each time a task
|
||||
# with the same UUID is executed when `serial` is specified in a playbook.
|
||||
# elapsed: Elapsed time since the first serialized task was started
|
||||
self.current = task._uuid
|
||||
self.stats[self.current] = {'time': time.time(), 'name': task.get_name()}
|
||||
dtn = datetime.now().astimezone()
|
||||
if self.current not in self.stats:
|
||||
self.stats[self.current] = {'started': dtn, 'elapsed': 0.0, 'name': task.get_name()}
|
||||
else:
|
||||
self.stats[self.current]['started'] = dtn
|
||||
if self._display.verbosity >= 2:
|
||||
self.stats[self.current]['path'] = task.get_path()
|
||||
|
||||
@@ -162,11 +209,11 @@ class CallbackModule(CallbackBase):
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
self._record_task(task)
|
||||
|
||||
def playbook_on_setup(self):
|
||||
self._display.display(tasktime())
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
# Align summary report header with other callback plugin summary
|
||||
self._display.banner("TASKS RECAP")
|
||||
|
||||
def playbook_on_stats(self, stats):
|
||||
self._display.display(tasktime())
|
||||
self._display.display(tasktime(self))
|
||||
self._display.display(filled("", fchar="="))
|
||||
|
||||
timestamp(self)
|
||||
@@ -178,7 +225,7 @@ class CallbackModule(CallbackBase):
|
||||
if self.sort_order is not None:
|
||||
results = sorted(
|
||||
self.stats.items(),
|
||||
key=lambda x: x[1]['time'],
|
||||
key=lambda x: x[1]['elapsed'],
|
||||
reverse=self.sort_order,
|
||||
)
|
||||
|
||||
@@ -187,7 +234,7 @@ class CallbackModule(CallbackBase):
|
||||
|
||||
# Print the timings
|
||||
for uuid, result in results:
|
||||
msg = u"{0:-<{2}}{1:->9}".format(result['name'] + u' ', u' {0:.02f}s'.format(result['time']), self._display.columns - 9)
|
||||
msg = u"{0:-<{2}}{1:->9}".format(result['name'] + u' ', u' {0:.02f}s'.format(result['elapsed']), self._display.columns - 9)
|
||||
if 'path' in result:
|
||||
msg += u"\n{0:-<{1}}".format(result['path'] + u' ', self._display.columns)
|
||||
self._display.display(msg)
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
# (c) 2017 Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Make coding more python3-ish
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: skippy
|
||||
callback_type: stdout
|
||||
requirements:
|
||||
- set as main display callback
|
||||
short_description: Ansible screen output that ignores skipped status
|
||||
deprecated:
|
||||
why: The 'default' callback plugin now supports this functionality
|
||||
removed_in: '2.11'
|
||||
alternative: "'default' callback plugin with 'display_skipped_hosts = no' option"
|
||||
extends_documentation_fragment:
|
||||
- default_callback
|
||||
description:
|
||||
- This callback does the same as the default except it does not output skipped host/task/item status
|
||||
'''
|
||||
|
||||
from ansible.plugins.callback.default import CallbackModule as CallbackModule_default
|
||||
|
||||
|
||||
class CallbackModule(CallbackModule_default):
|
||||
|
||||
'''
|
||||
This is the default callback interface, which simply prints messages
|
||||
to stdout when new callback events are received.
|
||||
'''
|
||||
|
||||
CALLBACK_VERSION = 2.0
|
||||
CALLBACK_TYPE = 'stdout'
|
||||
CALLBACK_NAME = 'ansible.posix.skippy'
|
||||
|
||||
def v2_runner_on_skipped(self, result):
|
||||
pass
|
||||
|
||||
def v2_runner_item_on_skipped(self, result):
|
||||
pass
|
||||
@@ -6,8 +6,8 @@ from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
callback: timer
|
||||
callback_type: aggregate
|
||||
name: timer
|
||||
type: aggregate
|
||||
requirements:
|
||||
- whitelist in configuration
|
||||
short_description: Adds time to play stats
|
||||
@@ -46,4 +46,6 @@ class CallbackModule(CallbackBase):
|
||||
def v2_playbook_on_stats(self, stats):
|
||||
end_time = datetime.utcnow()
|
||||
runtime = end_time - self.start_time
|
||||
self._display.display("Playbook run took %s days, %s hours, %s minutes, %s seconds" % (self.days_hours_minutes_seconds(runtime)))
|
||||
# Align summary report header with other callback plugin summary
|
||||
self._display.banner("PLAYBOOK RECAP")
|
||||
self._display.display("Playbook run took %s days, %s hours, %s minutes, %s seconds\n\r" % (self.days_hours_minutes_seconds(runtime)))
|
||||
|
||||
45
plugins/module_utils/_respawn.py
Normal file
45
plugins/module_utils/_respawn.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2023 Maxwell G <maxwell@gtmx.me>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""
|
||||
Helpers to respawn a module to run using the system interpreter
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
try:
|
||||
from ansible.module_utils.common import respawn
|
||||
except ImportError:
|
||||
HAS_RESPAWN_UTIL = False
|
||||
else:
|
||||
HAS_RESPAWN_UTIL = True
|
||||
|
||||
|
||||
SYSTEM_PYTHON_INTERPRETERS = (
|
||||
"/usr/bin/libexec/platform-python",
|
||||
"/usr/bin/python3",
|
||||
"/usr/bin/python2",
|
||||
"/usr/bin/python",
|
||||
)
|
||||
|
||||
|
||||
def respawn_module(module):
|
||||
"""
|
||||
Respawn an ansible module to using the first interpreter in
|
||||
SYSTEM_PYTHON_INTERPRETERS that contains `module`.
|
||||
|
||||
Args:
|
||||
module (str): Name of python module to search for
|
||||
|
||||
Returns:
|
||||
Returns None if the module cannot be respawned.
|
||||
"""
|
||||
if respawn.has_respawned():
|
||||
return
|
||||
interpreter = respawn.probe_interpreters_for_module(
|
||||
SYSTEM_PYTHON_INTERPRETERS, module
|
||||
)
|
||||
if interpreter:
|
||||
respawn.respawn_module(interpreter)
|
||||
344
plugins/module_utils/_version.py
Normal file
344
plugins/module_utils/_version.py
Normal file
@@ -0,0 +1,344 @@
|
||||
# Vendored copy of distutils/version.py from CPython 3.9.5
|
||||
#
|
||||
# Implements multiple version numbering conventions for the
|
||||
# Python Module Distribution Utilities.
|
||||
#
|
||||
# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
|
||||
#
|
||||
|
||||
"""Provides classes to represent module version numbers (one class for
|
||||
each style of version numbering). There are currently two such classes
|
||||
implemented: StrictVersion and LooseVersion.
|
||||
|
||||
Every version number class implements the following interface:
|
||||
* the 'parse' method takes a string and parses it to some internal
|
||||
representation; if the string is an invalid version number,
|
||||
'parse' raises a ValueError exception
|
||||
* the class constructor takes an optional string argument which,
|
||||
if supplied, is passed to 'parse'
|
||||
* __str__ reconstructs the string that was passed to 'parse' (or
|
||||
an equivalent string -- ie. one that will generate an equivalent
|
||||
version number instance)
|
||||
* __repr__ generates Python code to recreate the version number instance
|
||||
* _cmp compares the current instance with either another instance
|
||||
of the same class or a string (which will be parsed to an instance
|
||||
of the same class, thus must follow the same rules)
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import re
|
||||
|
||||
try:
|
||||
RE_FLAGS = re.VERBOSE | re.ASCII
|
||||
except AttributeError:
|
||||
RE_FLAGS = re.VERBOSE
|
||||
|
||||
|
||||
class Version:
|
||||
"""Abstract base class for version numbering classes. Just provides
|
||||
constructor (__init__) and reproducer (__repr__), because those
|
||||
seem to be the same for all version numbering classes; and route
|
||||
rich comparisons to _cmp.
|
||||
"""
|
||||
|
||||
def __init__(self, vstring=None):
|
||||
if vstring:
|
||||
self.parse(vstring)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s ('%s')" % (self.__class__.__name__, str(self))
|
||||
|
||||
def __eq__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c == 0
|
||||
|
||||
def __lt__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c < 0
|
||||
|
||||
def __le__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c <= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c > 0
|
||||
|
||||
def __ge__(self, other):
|
||||
c = self._cmp(other)
|
||||
if c is NotImplemented:
|
||||
return c
|
||||
return c >= 0
|
||||
|
||||
|
||||
# Interface for version-number classes -- must be implemented
|
||||
# by the following classes (the concrete ones -- Version should
|
||||
# be treated as an abstract class).
|
||||
# __init__ (string) - create and take same action as 'parse'
|
||||
# (string parameter is optional)
|
||||
# parse (string) - convert a string representation to whatever
|
||||
# internal representation is appropriate for
|
||||
# this style of version numbering
|
||||
# __str__ (self) - convert back to a string; should be very similar
|
||||
# (if not identical to) the string supplied to parse
|
||||
# __repr__ (self) - generate Python code to recreate
|
||||
# the instance
|
||||
# _cmp (self, other) - compare two version numbers ('other' may
|
||||
# be an unparsed version string, or another
|
||||
# instance of your version class)
|
||||
|
||||
|
||||
class StrictVersion(Version):
|
||||
"""Version numbering for anal retentives and software idealists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of two or three
|
||||
dot-separated numeric components, with an optional "pre-release" tag
|
||||
on the end. The pre-release tag consists of the letter 'a' or 'b'
|
||||
followed by a number. If the numeric components of two version
|
||||
numbers are equal, then one with a pre-release tag will always
|
||||
be deemed earlier (lesser) than one without.
|
||||
|
||||
The following are valid version numbers (shown in the order that
|
||||
would be obtained by sorting according to the supplied cmp function):
|
||||
|
||||
0.4 0.4.0 (these two are equivalent)
|
||||
0.4.1
|
||||
0.5a1
|
||||
0.5b3
|
||||
0.5
|
||||
0.9.6
|
||||
1.0
|
||||
1.0.4a3
|
||||
1.0.4b1
|
||||
1.0.4
|
||||
|
||||
The following are examples of invalid version numbers:
|
||||
|
||||
1
|
||||
2.7.2.2
|
||||
1.3.a4
|
||||
1.3pl1
|
||||
1.3c4
|
||||
|
||||
The rationale for this version numbering system will be explained
|
||||
in the distutils documentation.
|
||||
"""
|
||||
|
||||
version_re = re.compile(r"^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$", RE_FLAGS)
|
||||
|
||||
def parse(self, vstring):
|
||||
match = self.version_re.match(vstring)
|
||||
if not match:
|
||||
raise ValueError("invalid version number '%s'" % vstring)
|
||||
|
||||
(major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6)
|
||||
|
||||
if patch:
|
||||
self.version = tuple(map(int, [major, minor, patch]))
|
||||
else:
|
||||
self.version = tuple(map(int, [major, minor])) + (0,)
|
||||
|
||||
if prerelease:
|
||||
self.prerelease = (prerelease[0], int(prerelease_num))
|
||||
else:
|
||||
self.prerelease = None
|
||||
|
||||
def __str__(self):
|
||||
if self.version[2] == 0:
|
||||
vstring = ".".join(map(str, self.version[0:2]))
|
||||
else:
|
||||
vstring = ".".join(map(str, self.version))
|
||||
|
||||
if self.prerelease:
|
||||
vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
|
||||
|
||||
return vstring
|
||||
|
||||
def _cmp(self, other):
|
||||
if isinstance(other, str):
|
||||
other = StrictVersion(other)
|
||||
elif not isinstance(other, StrictVersion):
|
||||
return NotImplemented
|
||||
|
||||
if self.version != other.version:
|
||||
# numeric versions don't match
|
||||
# prerelease stuff doesn't matter
|
||||
if self.version < other.version:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
|
||||
# have to compare prerelease
|
||||
# case 1: neither has prerelease; they're equal
|
||||
# case 2: self has prerelease, other doesn't; other is greater
|
||||
# case 3: self doesn't have prerelease, other does: self is greater
|
||||
# case 4: both have prerelease: must compare them!
|
||||
|
||||
if not self.prerelease and not other.prerelease:
|
||||
return 0
|
||||
elif self.prerelease and not other.prerelease:
|
||||
return -1
|
||||
elif not self.prerelease and other.prerelease:
|
||||
return 1
|
||||
elif self.prerelease and other.prerelease:
|
||||
if self.prerelease == other.prerelease:
|
||||
return 0
|
||||
elif self.prerelease < other.prerelease:
|
||||
return -1
|
||||
else:
|
||||
return 1
|
||||
else:
|
||||
raise AssertionError("never get here")
|
||||
|
||||
|
||||
# end class StrictVersion
|
||||
|
||||
# The rules according to Greg Stein:
|
||||
# 1) a version number has 1 or more numbers separated by a period or by
|
||||
# sequences of letters. If only periods, then these are compared
|
||||
# left-to-right to determine an ordering.
|
||||
# 2) sequences of letters are part of the tuple for comparison and are
|
||||
# compared lexicographically
|
||||
# 3) recognize the numeric components may have leading zeroes
|
||||
#
|
||||
# The LooseVersion class below implements these rules: a version number
|
||||
# string is split up into a tuple of integer and string components, and
|
||||
# comparison is a simple tuple comparison. This means that version
|
||||
# numbers behave in a predictable and obvious way, but a way that might
|
||||
# not necessarily be how people *want* version numbers to behave. There
|
||||
# wouldn't be a problem if people could stick to purely numeric version
|
||||
# numbers: just split on period and compare the numbers as tuples.
|
||||
# However, people insist on putting letters into their version numbers;
|
||||
# the most common purpose seems to be:
|
||||
# - indicating a "pre-release" version
|
||||
# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
|
||||
# - indicating a post-release patch ('p', 'pl', 'patch')
|
||||
# but of course this can't cover all version number schemes, and there's
|
||||
# no way to know what a programmer means without asking him.
|
||||
#
|
||||
# The problem is what to do with letters (and other non-numeric
|
||||
# characters) in a version number. The current implementation does the
|
||||
# obvious and predictable thing: keep them as strings and compare
|
||||
# lexically within a tuple comparison. This has the desired effect if
|
||||
# an appended letter sequence implies something "post-release":
|
||||
# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
|
||||
#
|
||||
# However, if letters in a version number imply a pre-release version,
|
||||
# the "obvious" thing isn't correct. Eg. you would expect that
|
||||
# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
|
||||
# implemented here, this just isn't so.
|
||||
#
|
||||
# Two possible solutions come to mind. The first is to tie the
|
||||
# comparison algorithm to a particular set of semantic rules, as has
|
||||
# been done in the StrictVersion class above. This works great as long
|
||||
# as everyone can go along with bondage and discipline. Hopefully a
|
||||
# (large) subset of Python module programmers will agree that the
|
||||
# particular flavour of bondage and discipline provided by StrictVersion
|
||||
# provides enough benefit to be worth using, and will submit their
|
||||
# version numbering scheme to its domination. The free-thinking
|
||||
# anarchists in the lot will never give in, though, and something needs
|
||||
# to be done to accommodate them.
|
||||
#
|
||||
# Perhaps a "moderately strict" version class could be implemented that
|
||||
# lets almost anything slide (syntactically), and makes some heuristic
|
||||
# assumptions about non-digits in version number strings. This could
|
||||
# sink into special-case-hell, though; if I was as talented and
|
||||
# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
|
||||
# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
|
||||
# just as happy dealing with things like "2g6" and "1.13++". I don't
|
||||
# think I'm smart enough to do it right though.
|
||||
#
|
||||
# In any case, I've coded the test suite for this module (see
|
||||
# ../test/test_version.py) specifically to fail on things like comparing
|
||||
# "1.2a2" and "1.2". That's not because the *code* is doing anything
|
||||
# wrong, it's because the simple, obvious design doesn't match my
|
||||
# complicated, hairy expectations for real-world version numbers. It
|
||||
# would be a snap to fix the test suite to say, "Yep, LooseVersion does
|
||||
# the Right Thing" (ie. the code matches the conception). But I'd rather
|
||||
# have a conception that matches common notions about version numbers.
|
||||
|
||||
|
||||
class LooseVersion(Version):
|
||||
"""Version numbering for anarchists and software realists.
|
||||
Implements the standard interface for version number classes as
|
||||
described above. A version number consists of a series of numbers,
|
||||
separated by either periods or strings of letters. When comparing
|
||||
version numbers, the numeric components will be compared
|
||||
numerically, and the alphabetic components lexically. The following
|
||||
are all valid version numbers, in no particular order:
|
||||
|
||||
1.5.1
|
||||
1.5.2b2
|
||||
161
|
||||
3.10a
|
||||
8.02
|
||||
3.4j
|
||||
1996.07.12
|
||||
3.2.pl0
|
||||
3.1.1.6
|
||||
2g6
|
||||
11g
|
||||
0.960923
|
||||
2.2beta29
|
||||
1.13++
|
||||
5.5.kw
|
||||
2.0b1pl0
|
||||
|
||||
In fact, there is no such thing as an invalid version number under
|
||||
this scheme; the rules for comparison are simple and predictable,
|
||||
but may not always give the results you want (for some definition
|
||||
of "want").
|
||||
"""
|
||||
|
||||
component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE)
|
||||
|
||||
def __init__(self, vstring=None):
|
||||
if vstring:
|
||||
self.parse(vstring)
|
||||
|
||||
def parse(self, vstring):
|
||||
# I've given up on thinking I can reconstruct the version string
|
||||
# from the parsed tuple -- so I just store the string here for
|
||||
# use by __str__
|
||||
self.vstring = vstring
|
||||
components = [x for x in self.component_re.split(vstring) if x and x != "."]
|
||||
for i, obj in enumerate(components):
|
||||
try:
|
||||
components[i] = int(obj)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.version = components
|
||||
|
||||
def __str__(self):
|
||||
return self.vstring
|
||||
|
||||
def __repr__(self):
|
||||
return "LooseVersion ('%s')" % str(self)
|
||||
|
||||
def _cmp(self, other):
|
||||
if isinstance(other, str):
|
||||
other = LooseVersion(other)
|
||||
elif not isinstance(other, LooseVersion):
|
||||
return NotImplemented
|
||||
|
||||
if self.version == other.version:
|
||||
return 0
|
||||
if self.version < other.version:
|
||||
return -1
|
||||
if self.version > other.version:
|
||||
return 1
|
||||
|
||||
|
||||
# end class LooseVersion
|
||||
322
plugins/module_utils/firewalld.py
Normal file
322
plugins/module_utils/firewalld.py
Normal file
@@ -0,0 +1,322 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# (c) 2013-2018, Adam Miller (maxamillion@fedoraproject.org)
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from ansible_collections.ansible.posix.plugins.module_utils.version import LooseVersion
|
||||
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
FW_VERSION = None
|
||||
fw = None
|
||||
fw_offline = False
|
||||
import_failure = True
|
||||
try:
|
||||
import firewall.config
|
||||
FW_VERSION = firewall.config.VERSION
|
||||
|
||||
from firewall.client import FirewallClient
|
||||
from firewall.client import FirewallClientZoneSettings
|
||||
from firewall.errors import FirewallError
|
||||
import_failure = False
|
||||
|
||||
try:
|
||||
fw = FirewallClient()
|
||||
fw.getDefaultZone()
|
||||
|
||||
except (AttributeError, FirewallError):
|
||||
# Firewalld is not currently running, permanent-only operations
|
||||
fw_offline = True
|
||||
|
||||
# Import other required parts of the firewalld API
|
||||
#
|
||||
# NOTE:
|
||||
# online and offline operations do not share a common firewalld API
|
||||
try:
|
||||
from firewall.core.fw_test import Firewall_test
|
||||
fw = Firewall_test()
|
||||
except (ModuleNotFoundError):
|
||||
# In firewalld version 0.7.0 this behavior changed
|
||||
from firewall.core.fw import Firewall
|
||||
fw = Firewall(offline=True)
|
||||
|
||||
fw.start()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class FirewallTransaction(object):
|
||||
"""
|
||||
FirewallTransaction
|
||||
|
||||
This is the base class for all firewalld transactions we might want to have
|
||||
"""
|
||||
|
||||
def __init__(self, module, action_args=(), zone=None, desired_state=None,
|
||||
permanent=False, immediate=False, enabled_values=None, disabled_values=None):
|
||||
# type: (firewall.client, tuple, str, bool, bool, bool)
|
||||
"""
|
||||
initializer the transaction
|
||||
|
||||
:module: AnsibleModule, instance of AnsibleModule
|
||||
:action_args: tuple, args to pass for the action to take place
|
||||
:zone: str, firewall zone
|
||||
:desired_state: str, the desired state (enabled, disabled, etc)
|
||||
:permanent: bool, action should be permanent
|
||||
:immediate: bool, action should take place immediately
|
||||
:enabled_values: str[], acceptable values for enabling something (default: enabled)
|
||||
:disabled_values: str[], acceptable values for disabling something (default: disabled)
|
||||
"""
|
||||
|
||||
self.module = module
|
||||
self.fw = fw
|
||||
self.action_args = action_args
|
||||
|
||||
if zone:
|
||||
self.zone = zone
|
||||
else:
|
||||
if fw_offline:
|
||||
self.zone = fw.get_default_zone()
|
||||
else:
|
||||
self.zone = fw.getDefaultZone()
|
||||
|
||||
self.desired_state = desired_state
|
||||
self.permanent = permanent
|
||||
self.immediate = immediate
|
||||
self.fw_offline = fw_offline
|
||||
self.enabled_values = enabled_values or ["enabled"]
|
||||
self.disabled_values = disabled_values or ["disabled"]
|
||||
|
||||
# List of messages that we'll call module.fail_json or module.exit_json
|
||||
# with.
|
||||
self.msgs = []
|
||||
|
||||
# Allow for custom messages to be added for certain subclass transaction
|
||||
# types
|
||||
self.enabled_msg = None
|
||||
self.disabled_msg = None
|
||||
|
||||
#####################
|
||||
# exception handling
|
||||
#
|
||||
def action_handler(self, action_func, action_func_args):
|
||||
"""
|
||||
Function to wrap calls to make actions on firewalld in try/except
|
||||
logic and emit (hopefully) useful error messages
|
||||
"""
|
||||
|
||||
try:
|
||||
return action_func(*action_func_args)
|
||||
except Exception as e:
|
||||
|
||||
# If there are any commonly known errors that we should provide more
|
||||
# context for to help the users diagnose what's wrong. Handle that here
|
||||
if "INVALID_SERVICE" in "%s" % e:
|
||||
self.msgs.append("Services are defined by port/tcp relationship and named as they are in /etc/services (on most systems)")
|
||||
|
||||
if len(self.msgs) > 0:
|
||||
self.module.fail_json(
|
||||
msg='ERROR: Exception caught: %s %s' % (e, ', '.join(self.msgs))
|
||||
)
|
||||
else:
|
||||
self.module.fail_json(msg='ERROR: Exception caught: %s' % e)
|
||||
|
||||
def get_fw_zone_settings(self):
|
||||
if self.fw_offline:
|
||||
fw_zone = self.fw.config.get_zone(self.zone)
|
||||
fw_settings = FirewallClientZoneSettings(
|
||||
list(self.fw.config.get_zone_config(fw_zone))
|
||||
)
|
||||
else:
|
||||
fw_zone = self.fw.config().getZoneByName(self.zone)
|
||||
fw_settings = fw_zone.getSettings()
|
||||
|
||||
return (fw_zone, fw_settings)
|
||||
|
||||
def update_fw_settings(self, fw_zone, fw_settings):
|
||||
if self.fw_offline:
|
||||
self.fw.config.set_zone_config(fw_zone, fw_settings.settings)
|
||||
else:
|
||||
fw_zone.update(fw_settings)
|
||||
|
||||
def get_enabled_immediate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_enabled_permanent(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_enabled_immediate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_enabled_permanent(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_disabled_immediate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_disabled_permanent(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
run
|
||||
|
||||
This function contains the "transaction logic" where as all operations
|
||||
follow a similar pattern in order to perform their action but simply
|
||||
call different functions to carry that action out.
|
||||
"""
|
||||
|
||||
self.changed = False
|
||||
|
||||
if self.immediate and self.permanent:
|
||||
is_enabled_permanent = self.action_handler(
|
||||
self.get_enabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
is_enabled_immediate = self.action_handler(
|
||||
self.get_enabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.msgs.append('Permanent and Non-Permanent(immediate) operation')
|
||||
|
||||
if self.desired_state in self.enabled_values:
|
||||
if not is_enabled_permanent or not is_enabled_immediate:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
if not is_enabled_permanent:
|
||||
self.action_handler(
|
||||
self.set_enabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if not is_enabled_immediate:
|
||||
self.action_handler(
|
||||
self.set_enabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.enabled_msg:
|
||||
self.msgs.append(self.enabled_msg)
|
||||
|
||||
elif self.desired_state in self.disabled_values:
|
||||
if is_enabled_permanent or is_enabled_immediate:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
if is_enabled_permanent:
|
||||
self.action_handler(
|
||||
self.set_disabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if is_enabled_immediate:
|
||||
self.action_handler(
|
||||
self.set_disabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.disabled_msg:
|
||||
self.msgs.append(self.disabled_msg)
|
||||
|
||||
elif self.permanent and not self.immediate:
|
||||
is_enabled = self.action_handler(
|
||||
self.get_enabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
self.msgs.append('Permanent operation')
|
||||
|
||||
if self.desired_state in self.enabled_values:
|
||||
if not is_enabled:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
|
||||
self.action_handler(
|
||||
self.set_enabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.enabled_msg:
|
||||
self.msgs.append(self.enabled_msg)
|
||||
|
||||
elif self.desired_state in self.disabled_values:
|
||||
if is_enabled:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
|
||||
self.action_handler(
|
||||
self.set_disabled_permanent,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.disabled_msg:
|
||||
self.msgs.append(self.disabled_msg)
|
||||
|
||||
elif self.immediate and not self.permanent:
|
||||
is_enabled = self.action_handler(
|
||||
self.get_enabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.msgs.append('Non-permanent operation')
|
||||
|
||||
if self.desired_state in self.enabled_values:
|
||||
if not is_enabled:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
|
||||
self.action_handler(
|
||||
self.set_enabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.enabled_msg:
|
||||
self.msgs.append(self.enabled_msg)
|
||||
|
||||
elif self.desired_state in self.disabled_values:
|
||||
if is_enabled:
|
||||
if self.module.check_mode:
|
||||
self.module.exit_json(changed=True)
|
||||
|
||||
self.action_handler(
|
||||
self.set_disabled_immediate,
|
||||
self.action_args
|
||||
)
|
||||
self.changed = True
|
||||
if self.changed and self.disabled_msg:
|
||||
self.msgs.append(self.disabled_msg)
|
||||
|
||||
return (self.changed, self.msgs)
|
||||
|
||||
@staticmethod
|
||||
def sanity_check(module):
|
||||
"""
|
||||
Perform sanity checking, version checks, etc
|
||||
|
||||
:module: AnsibleModule instance
|
||||
"""
|
||||
|
||||
if FW_VERSION and fw_offline:
|
||||
# Pre-run version checking
|
||||
if LooseVersion(FW_VERSION) < LooseVersion("0.3.9"):
|
||||
module.fail_json(msg='unsupported version of firewalld, offline operations require >= 0.3.9 - found: {0}'.format(FW_VERSION))
|
||||
elif FW_VERSION and not fw_offline:
|
||||
# Pre-run version checking
|
||||
if LooseVersion(FW_VERSION) < LooseVersion("0.2.11"):
|
||||
module.fail_json(msg='unsupported version of firewalld, requires >= 0.2.11 - found: {0}'.format(FW_VERSION))
|
||||
|
||||
# Check for firewalld running
|
||||
try:
|
||||
if fw.connected is False:
|
||||
module.fail_json(msg='firewalld service must be running, or try with offline=true')
|
||||
except AttributeError:
|
||||
module.fail_json(msg="firewalld connection can't be established,\
|
||||
installed version (%s) likely too old. Requires firewalld >= 0.2.11" % FW_VERSION)
|
||||
|
||||
if import_failure:
|
||||
if HAS_RESPAWN_UTIL:
|
||||
respawn_module("firewall")
|
||||
module.fail_json(
|
||||
msg=missing_required_lib('firewall') + '. Version 0.2.11 or newer required (0.3.9 or newer for offline operations)'
|
||||
)
|
||||
@@ -48,6 +48,10 @@
|
||||
# agrees to be bound by the terms and conditions of this License
|
||||
# Agreement.
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
|
||||
|
||||
20
plugins/module_utils/version.py
Normal file
20
plugins/module_utils/version.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2021, Felix Fontein <felix@fontein.de>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
"""Provide version object to compare version numbers."""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
# Once we drop support for Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, we can
|
||||
# remove the _version.py file, and replace the following import by
|
||||
#
|
||||
# from ansible.module_utils.compat.version import LooseVersion
|
||||
|
||||
from ._version import LooseVersion, StrictVersion
|
||||
|
||||
__all__ = ['LooseVersion', 'StrictVersion']
|
||||
@@ -14,40 +14,47 @@ module: acl
|
||||
short_description: Set and retrieve file ACL information.
|
||||
description:
|
||||
- Set and retrieve file ACL information.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- The full path of the file or object.
|
||||
type: path
|
||||
required: yes
|
||||
required: true
|
||||
aliases: [ name ]
|
||||
state:
|
||||
description:
|
||||
- Define whether the ACL should be present or not.
|
||||
- The C(query) state gets the current ACL without changing it, for use in C(register) operations.
|
||||
- The V(query) state gets the current ACL without changing it, for use in C(register) operations.
|
||||
choices: [ absent, present, query ]
|
||||
default: query
|
||||
type: str
|
||||
follow:
|
||||
description:
|
||||
- Whether to follow symlinks on the path if a symlink is encountered.
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
default:
|
||||
description:
|
||||
- If the target is a directory, setting this to C(yes) will make it the default ACL for entities created inside the directory.
|
||||
- Setting C(default) to C(yes) causes an error if the path is a file.
|
||||
- If O(path) is a directory, setting this to V(true) will make it the default ACL for entities created inside the directory.
|
||||
- Setting O(default=true) causes an error if O(path) is a file.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
entity:
|
||||
description:
|
||||
- The actual user or group that the ACL applies to when matching entity types user or group are selected.
|
||||
type: str
|
||||
default: ""
|
||||
etype:
|
||||
description:
|
||||
- The entity type of the ACL to apply, see C(setfacl) documentation for more info.
|
||||
choices: [ group, mask, other, user ]
|
||||
type: str
|
||||
permissions:
|
||||
description:
|
||||
- The permissions to apply/remove can be any combination of C(r), C(w) and C(x) (read, write and execute respectively)
|
||||
- The permissions to apply/remove can be any combination of C(r), C(w), C(x)
|
||||
(read, write and execute respectively), and C(X) (execute permission if the file is a directory or already has execute permission for some user)
|
||||
type: str
|
||||
entry:
|
||||
description:
|
||||
- DEPRECATED.
|
||||
@@ -56,31 +63,39 @@ options:
|
||||
- The qualifier may be empty for some types, but the type and perms are always required.
|
||||
- C(-) can be used as placeholder when you do not care about permissions.
|
||||
- This is now superseded by entity, type and permissions fields.
|
||||
type: str
|
||||
recursive:
|
||||
description:
|
||||
- Recursively sets the specified ACL.
|
||||
- Incompatible with C(state=query).
|
||||
- Incompatible with O(state=query).
|
||||
- Alias O(recurse) added in version 1.3.0.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
aliases: [ recurse ]
|
||||
use_nfsv4_acls:
|
||||
description:
|
||||
- Use NFSv4 ACLs instead of POSIX ACLs.
|
||||
- This feature uses C(nfs4_setfacl) and C(nfs4_getfacl). The behavior depends on those implementation.
|
||||
And currently it only supports C(A) in ACE, so C(D) must be replaced with the appropriate C(A).
|
||||
- Permission is set as optimised ACLs by the system. You can check the actual ACLs that has been set using the return value.
|
||||
- More info C(man nfs4_setfacl)
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
recalculate_mask:
|
||||
description:
|
||||
- Select if and when to recalculate the effective right masks of the files.
|
||||
- See C(setfacl) documentation for more info.
|
||||
- Incompatible with C(state=query).
|
||||
- Incompatible with O(state=query).
|
||||
choices: [ default, mask, no_mask ]
|
||||
default: default
|
||||
type: str
|
||||
author:
|
||||
- Brian Coca (@bcoca)
|
||||
- Jérémie Astori (@astorije)
|
||||
notes:
|
||||
- The C(acl) module requires that ACLs are enabled on the target filesystem and that the C(setfacl) and C(getfacl) binaries are installed.
|
||||
- The M(ansible.posix.acl) module requires that ACLs are enabled on the target filesystem and that the C(setfacl) and C(getfacl) binaries are installed.
|
||||
- As of Ansible 2.0, this module only supports Linux distributions.
|
||||
- As of Ansible 2.3, the I(name) option has been changed to I(path) as default, but I(name) still works as well.
|
||||
- As of Ansible 2.3, the O(name) option has been changed to O(path) as default, but O(name) still works as well.
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -105,7 +120,7 @@ EXAMPLES = r'''
|
||||
entity: joe
|
||||
etype: user
|
||||
permissions: rw
|
||||
default: yes
|
||||
default: true
|
||||
state: present
|
||||
|
||||
- name: Same as previous but using entry shorthand
|
||||
@@ -168,7 +183,7 @@ def split_entry(entry):
|
||||
def build_entry(etype, entity, permissions=None, use_nfsv4_acls=False):
|
||||
'''Builds and returns an entry string. Does not include the permissions bit if they are not provided.'''
|
||||
if use_nfsv4_acls:
|
||||
return ':'.join([etype, entity, permissions, 'allow'])
|
||||
return ':'.join(['A', 'g' if etype == 'group' else '', entity, permissions + 'tcy'])
|
||||
|
||||
if permissions:
|
||||
return etype + ':' + entity + ':' + permissions
|
||||
@@ -176,22 +191,27 @@ def build_entry(etype, entity, permissions=None, use_nfsv4_acls=False):
|
||||
return etype + ':' + entity
|
||||
|
||||
|
||||
def build_command(module, mode, path, follow, default, recursive, recalculate_mask, entry=''):
|
||||
def build_command(module, mode, path, follow, default, recursive, recalculate_mask, use_nfsv4_acls, entry=''):
|
||||
'''Builds and returns a getfacl/setfacl command.'''
|
||||
if mode == 'set':
|
||||
cmd = [module.get_bin_path('setfacl', True)]
|
||||
cmd.extend(['-m', entry])
|
||||
cmd = [module.get_bin_path('nfs4_setfacl' if use_nfsv4_acls else 'setfacl', True)]
|
||||
cmd.extend(['-a' if use_nfsv4_acls else '-m', entry])
|
||||
elif mode == 'rm':
|
||||
cmd = [module.get_bin_path('setfacl', True)]
|
||||
cmd = [module.get_bin_path('nfs4_setfacl' if use_nfsv4_acls else 'setfacl', True)]
|
||||
cmd.extend(['-x', entry])
|
||||
else: # mode == 'get'
|
||||
cmd = [module.get_bin_path('getfacl', True)]
|
||||
# prevents absolute path warnings and removes headers
|
||||
if platform.system().lower() == 'linux':
|
||||
if use_nfsv4_acls:
|
||||
# use nfs4_getfacl instead of getfacl if use_nfsv4_acls is True
|
||||
cmd = [module.get_bin_path('nfs4_getfacl', True)]
|
||||
else:
|
||||
cmd = [module.get_bin_path('getfacl', True)]
|
||||
cmd.append('--absolute-names')
|
||||
cmd.append('--omit-header')
|
||||
cmd.append('--absolute-names')
|
||||
|
||||
if recursive:
|
||||
if recursive and not use_nfsv4_acls:
|
||||
cmd.append('--recursive')
|
||||
|
||||
if recalculate_mask == 'mask' and mode in ['set', 'rm']:
|
||||
@@ -199,7 +219,7 @@ def build_command(module, mode, path, follow, default, recursive, recalculate_ma
|
||||
elif recalculate_mask == 'no_mask' and mode in ['set', 'rm']:
|
||||
cmd.append('--no-mask')
|
||||
|
||||
if not follow:
|
||||
if not follow and not use_nfsv4_acls:
|
||||
if platform.system().lower() == 'linux':
|
||||
cmd.append('--physical')
|
||||
elif platform.system().lower() == 'freebsd':
|
||||
@@ -212,24 +232,34 @@ def build_command(module, mode, path, follow, default, recursive, recalculate_ma
|
||||
return cmd
|
||||
|
||||
|
||||
def acl_changed(module, cmd):
|
||||
def acl_changed(module, cmd, entry, use_nfsv4_acls=False):
|
||||
'''Returns true if the provided command affects the existing ACLs, false otherwise.'''
|
||||
# FreeBSD do not have a --test flag, so by default, it is safer to always say "true"
|
||||
# To check the ACL changes, use the output of setfacl or nfs4_setfacl with '--test'.
|
||||
# FreeBSD do not have a --test flag, so by default, it is safer to always say "true".
|
||||
if platform.system().lower() == 'freebsd':
|
||||
return True
|
||||
|
||||
cmd = cmd[:] # lists are mutables so cmd would be overwritten without this
|
||||
cmd.insert(1, '--test')
|
||||
lines = run_acl(module, cmd)
|
||||
|
||||
counter = 0
|
||||
for line in lines:
|
||||
if not line.endswith('*,*'):
|
||||
return True
|
||||
return False
|
||||
if line.endswith('*,*') and not use_nfsv4_acls:
|
||||
return False
|
||||
# if use_nfsv4_acls and entry is listed
|
||||
if use_nfsv4_acls and entry == line:
|
||||
counter += 1
|
||||
|
||||
# The current 'nfs4_setfacl --test' lists a new entry,
|
||||
# which will be added at the top of list, followed by the existing entries.
|
||||
# So if the entry has already been registered, the entry should be find twice.
|
||||
if counter == 2:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_acl(module, cmd, check_rc=True):
|
||||
|
||||
'''Runs the provided command and returns the output as a list of lines.'''
|
||||
try:
|
||||
(rc, out, err) = module.run_command(cmd, check_rc=check_rc)
|
||||
except Exception as e:
|
||||
@@ -265,7 +295,7 @@ def main():
|
||||
),
|
||||
follow=dict(type='bool', default=True),
|
||||
default=dict(type='bool', default=False),
|
||||
recursive=dict(type='bool', default=False),
|
||||
recursive=dict(type='bool', default=False, aliases=['recurse']),
|
||||
recalculate_mask=dict(
|
||||
type='str',
|
||||
default='default',
|
||||
@@ -302,7 +332,7 @@ def main():
|
||||
module.fail_json(msg="'recalculate_mask' MUST NOT be set to 'mask' or 'no_mask' when 'state=query'.")
|
||||
|
||||
if not entry:
|
||||
if state == 'absent' and permissions:
|
||||
if state == 'absent' and permissions and not use_nfsv4_acls:
|
||||
module.fail_json(msg="'permissions' MUST NOT be set when 'state=absent'.")
|
||||
|
||||
if state == 'absent' and not entity:
|
||||
@@ -339,21 +369,24 @@ def main():
|
||||
entry = build_entry(etype, entity, permissions, use_nfsv4_acls)
|
||||
command = build_command(
|
||||
module, 'set', path, follow,
|
||||
default, recursive, recalculate_mask, entry
|
||||
default, recursive, recalculate_mask, use_nfsv4_acls, entry
|
||||
)
|
||||
changed = acl_changed(module, command)
|
||||
changed = acl_changed(module, command, entry, use_nfsv4_acls)
|
||||
|
||||
if changed and not module.check_mode:
|
||||
run_acl(module, command)
|
||||
msg = "%s is present" % entry
|
||||
|
||||
elif state == 'absent':
|
||||
entry = build_entry(etype, entity, use_nfsv4_acls)
|
||||
if use_nfsv4_acls:
|
||||
entry = build_entry(etype, entity, permissions, use_nfsv4_acls)
|
||||
else:
|
||||
entry = build_entry(etype, entity, use_nfsv4_acls)
|
||||
command = build_command(
|
||||
module, 'rm', path, follow,
|
||||
default, recursive, recalculate_mask, entry
|
||||
default, recursive, recalculate_mask, use_nfsv4_acls, entry
|
||||
)
|
||||
changed = acl_changed(module, command)
|
||||
changed = acl_changed(module, command, entry, use_nfsv4_acls)
|
||||
|
||||
if changed and not module.check_mode:
|
||||
run_acl(module, command, False)
|
||||
@@ -364,7 +397,10 @@ def main():
|
||||
|
||||
acl = run_acl(
|
||||
module,
|
||||
build_command(module, 'get', path, follow, default, recursive, recalculate_mask)
|
||||
build_command(
|
||||
module, 'get', path, follow, default, recursive,
|
||||
recalculate_mask, use_nfsv4_acls
|
||||
)
|
||||
)
|
||||
|
||||
module.exit_json(changed=changed, msg=msg, acl=acl)
|
||||
|
||||
@@ -8,13 +8,14 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: at
|
||||
short_description: Schedule the execution of a command or script file via the at command
|
||||
description:
|
||||
- Use this module to schedule a command or script file to run once in the future.
|
||||
- All jobs are executed in the 'a' queue.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
command:
|
||||
description:
|
||||
@@ -28,16 +29,14 @@ options:
|
||||
description:
|
||||
- The count of units in the future to execute the command or script file.
|
||||
type: int
|
||||
required: true
|
||||
units:
|
||||
description:
|
||||
- The type of units in the future to execute the command or script file.
|
||||
type: str
|
||||
required: true
|
||||
choices: [ minutes, hours, days, weeks ]
|
||||
state:
|
||||
description:
|
||||
- The state dictates if the command or script file should be evaluated as present(added) or absent(deleted).
|
||||
- The state dictates if the command or script file should be evaluated as V(present) (added) or V(absent) (deleted).
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
@@ -45,7 +44,7 @@ options:
|
||||
description:
|
||||
- If a matching job is present a new job will not be added.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
requirements:
|
||||
- at
|
||||
author:
|
||||
@@ -69,10 +68,11 @@ EXAMPLES = r'''
|
||||
command: ls -d / >/dev/null
|
||||
count: 20
|
||||
units: minutes
|
||||
unique: yes
|
||||
unique: true
|
||||
'''
|
||||
|
||||
import os
|
||||
import platform
|
||||
import tempfile
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
@@ -88,7 +88,7 @@ def add_job(module, result, at_cmd, count, units, command, script_file):
|
||||
|
||||
def delete_job(module, result, at_cmd, command, script_file):
|
||||
for matching_job in get_matching_jobs(module, at_cmd, script_file):
|
||||
at_command = "%s -d %s" % (at_cmd, matching_job)
|
||||
at_command = "%s -r %s" % (at_cmd, matching_job)
|
||||
rc, out, err = module.run_command(at_command, check_rc=True)
|
||||
result['changed'] = True
|
||||
if command:
|
||||
@@ -116,7 +116,8 @@ def get_matching_jobs(module, at_cmd, script_file):
|
||||
# If the script text is contained in a job add job number to list.
|
||||
for current_job in current_jobs:
|
||||
split_current_job = current_job.split()
|
||||
at_command = "%s -c %s" % (at_cmd, split_current_job[0])
|
||||
at_opt = '-c' if platform.system() != 'AIX' else '-lv'
|
||||
at_command = "%s %s %s" % (at_cmd, at_opt, split_current_job[0])
|
||||
rc, out, err = module.run_command(at_command, check_rc=True)
|
||||
if script_file_string in out:
|
||||
matching_jobs.append(split_current_job[0])
|
||||
@@ -128,7 +129,7 @@ def get_matching_jobs(module, at_cmd, script_file):
|
||||
def create_tempfile(command):
|
||||
filed, script_file = tempfile.mkstemp(prefix='at')
|
||||
fileh = os.fdopen(filed, 'w')
|
||||
fileh.write(command)
|
||||
fileh.write(command + os.linesep)
|
||||
fileh.close()
|
||||
return script_file
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ module: authorized_key
|
||||
short_description: Adds or removes an SSH authorized key
|
||||
description:
|
||||
- Adds or removes SSH authorized keys for particular user accounts.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
user:
|
||||
description:
|
||||
@@ -23,23 +24,26 @@ options:
|
||||
key:
|
||||
description:
|
||||
- The SSH public key(s), as a string or (since Ansible 1.9) url (https://github.com/username.keys).
|
||||
- You can also use V(file://) prefix to search remote for a file with SSH key(s).
|
||||
type: str
|
||||
required: true
|
||||
path:
|
||||
description:
|
||||
- Alternate path to the authorized_keys file.
|
||||
- When unset, this value defaults to I(~/.ssh/authorized_keys).
|
||||
- Alternative path to the authorized_keys file.
|
||||
- The default value is the V(.ssh/authorized_keys) of the home of the user specified in the O(user) parameter.
|
||||
- Most of the time, it is not necessary to set this key.
|
||||
- Use the path to your target authorized_keys if you need to explicitly point on it.
|
||||
type: path
|
||||
manage_dir:
|
||||
description:
|
||||
- Whether this module should manage the directory of the authorized key file.
|
||||
- If set to C(yes), the module will create the directory, as well as set the owner and permissions
|
||||
- If set to V(true), the module will create the directory, as well as set the owner and permissions
|
||||
of an existing directory.
|
||||
- Be sure to set C(manage_dir=no) if you are using an alternate directory for authorized_keys,
|
||||
as set with C(path), since you could lock yourself out of SSH access.
|
||||
- Be sure to set O(manage_dir=false) if you are using an alternate directory for authorized_keys,
|
||||
as set with O(path), since you could lock yourself out of SSH access.
|
||||
- See the example below.
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
state:
|
||||
description:
|
||||
- Whether the given key (with the given key_options) should or should not be in the file.
|
||||
@@ -49,22 +53,23 @@ options:
|
||||
key_options:
|
||||
description:
|
||||
- A string of ssh key options to be prepended to the key in the authorized_keys file.
|
||||
type: str
|
||||
exclusive:
|
||||
description:
|
||||
- Whether to remove all other non-specified keys from the authorized_keys file.
|
||||
- Multiple keys can be specified in a single C(key) string value by separating them by newlines.
|
||||
- Multiple keys can be specified in a single O(key) string value by separating them by newlines.
|
||||
- This option is not loop aware, so if you use C(with_) , it will be exclusive per iteration of the loop.
|
||||
- If you want multiple keys in the file you need to pass them all to C(key) in a single batch as mentioned above.
|
||||
- If you want multiple keys in the file you need to pass them all to O(key) in a single batch as mentioned above.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
validate_certs:
|
||||
description:
|
||||
- This only applies if using a https url as the source of the keys.
|
||||
- If set to C(no), the SSL certificates will not be validated.
|
||||
- This should only set to C(no) used on personally controlled sites using self-signed certificates as it avoids verifying the source site.
|
||||
- Prior to 2.1 the code worked as if this was set to C(yes).
|
||||
- If set to V(false), the SSL certificates will not be validated.
|
||||
- This should only set to V(false) used on personally controlled sites using self-signed certificates as it avoids verifying the source site.
|
||||
- Prior to 2.1 the code worked as if this was set to V(true).
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
comment:
|
||||
description:
|
||||
- Change the comment on the public key.
|
||||
@@ -75,7 +80,7 @@ options:
|
||||
description:
|
||||
- Follow path symlink instead of replacing it.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
author: Ansible Core Team
|
||||
'''
|
||||
|
||||
@@ -92,13 +97,25 @@ EXAMPLES = r'''
|
||||
state: present
|
||||
key: https://github.com/charlie.keys
|
||||
|
||||
- name: Set authorized keys taken from path on controller node
|
||||
ansible.posix.authorized_key:
|
||||
user: charlie
|
||||
state: present
|
||||
key: file:///home/charlie/.ssh/id_rsa.pub
|
||||
|
||||
- name: Set authorized keys taken from url using lookup
|
||||
ansible.posix.authorized_key:
|
||||
user: charlie
|
||||
state: present
|
||||
key: "{{ lookup('url', 'https://github.com/charlie.keys', split_lines=False) }}"
|
||||
|
||||
- name: Set authorized key in alternate location
|
||||
ansible.posix.authorized_key:
|
||||
user: charlie
|
||||
state: present
|
||||
key: "{{ lookup('file', '/home/charlie/.ssh/id_rsa.pub') }}"
|
||||
path: /etc/ssh/authorized_keys/charlie
|
||||
manage_dir: False
|
||||
manage_dir: false
|
||||
|
||||
- name: Set up multiple authorized keys
|
||||
ansible.posix.authorized_key:
|
||||
@@ -121,14 +138,14 @@ EXAMPLES = r'''
|
||||
user: charlie
|
||||
state: present
|
||||
key: https://github.com/user.keys
|
||||
validate_certs: False
|
||||
validate_certs: false
|
||||
|
||||
- name: Set authorized key, removing all the authorized keys already set
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ lookup('file', 'public_keys/doe-jane') }}"
|
||||
state: present
|
||||
exclusive: True
|
||||
exclusive: true
|
||||
|
||||
- name: Set authorized key for user ubuntu copying it from current user
|
||||
ansible.posix.authorized_key:
|
||||
@@ -142,7 +159,7 @@ exclusive:
|
||||
description: If the key has been forced to be exclusive or not.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: False
|
||||
sample: false
|
||||
key:
|
||||
description: The key that the module was running against.
|
||||
returned: success
|
||||
@@ -162,7 +179,7 @@ manage_dir:
|
||||
description: Whether this module managed the directory of the authorized key file.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: True
|
||||
sample: true
|
||||
path:
|
||||
description: Alternate path to the authorized_keys file
|
||||
returned: success
|
||||
@@ -184,7 +201,7 @@ user:
|
||||
type: str
|
||||
sample: user
|
||||
validate_certs:
|
||||
description: This only applies if using a https url as the source of the keys. If set to C(no), the SSL certificates will not be validated.
|
||||
description: This only applies if using a https url as the source of the keys. If set to C(false), the SSL certificates will not be validated.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: true
|
||||
@@ -213,6 +230,7 @@ from operator import itemgetter
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
|
||||
|
||||
class keydict(dict):
|
||||
@@ -326,7 +344,10 @@ def keyfile(module, user, write=False, path=None, manage_dir=True, follow=False)
|
||||
|
||||
if manage_dir:
|
||||
if not os.path.exists(sshdir):
|
||||
os.mkdir(sshdir, int('0700', 8))
|
||||
try:
|
||||
os.mkdir(sshdir, int('0700', 8))
|
||||
except OSError as e:
|
||||
module.fail_json(msg="Failed to create directory %s : %s" % (sshdir, to_native(e)))
|
||||
if module.selinux_enabled():
|
||||
module.set_default_selinux_context(sshdir, False)
|
||||
os.chown(sshdir, uid, gid)
|
||||
@@ -336,6 +357,8 @@ def keyfile(module, user, write=False, path=None, manage_dir=True, follow=False)
|
||||
basedir = os.path.dirname(keysfile)
|
||||
if not os.path.exists(basedir):
|
||||
os.makedirs(basedir)
|
||||
|
||||
f = None
|
||||
try:
|
||||
f = open(keysfile, "w") # touches file so we can set ownership and perms
|
||||
finally:
|
||||
@@ -383,12 +406,29 @@ def parsekey(module, raw_key, rank=None):
|
||||
'''
|
||||
|
||||
VALID_SSH2_KEY_TYPES = [
|
||||
'ssh-ed25519',
|
||||
'sk-ecdsa-sha2-nistp256@openssh.com',
|
||||
'sk-ecdsa-sha2-nistp256-cert-v01@openssh.com',
|
||||
'webauthn-sk-ecdsa-sha2-nistp256@openssh.com',
|
||||
'ecdsa-sha2-nistp256',
|
||||
'ecdsa-sha2-nistp256-cert-v01@openssh.com',
|
||||
'ecdsa-sha2-nistp384',
|
||||
'ecdsa-sha2-nistp384-cert-v01@openssh.com',
|
||||
'ecdsa-sha2-nistp521',
|
||||
'ecdsa-sha2-nistp521-cert-v01@openssh.com',
|
||||
'sk-ssh-ed25519@openssh.com',
|
||||
'sk-ssh-ed25519-cert-v01@openssh.com',
|
||||
'ssh-ed25519',
|
||||
'ssh-ed25519-cert-v01@openssh.com',
|
||||
'ssh-dss',
|
||||
'ssh-rsa',
|
||||
'ssh-xmss@openssh.com',
|
||||
'ssh-xmss-cert-v01@openssh.com',
|
||||
'rsa-sha2-256',
|
||||
'rsa-sha2-512',
|
||||
'ssh-rsa-cert-v01@openssh.com',
|
||||
'rsa-sha2-256-cert-v01@openssh.com',
|
||||
'rsa-sha2-512-cert-v01@openssh.com',
|
||||
'ssh-dss-cert-v01@openssh.com',
|
||||
]
|
||||
|
||||
options = None # connection options
|
||||
@@ -462,15 +502,14 @@ def parsekeys(module, lines):
|
||||
|
||||
|
||||
def writefile(module, filename, content):
|
||||
|
||||
fd, tmp_path = tempfile.mkstemp('', 'tmp', os.path.dirname(filename))
|
||||
f = open(tmp_path, "w")
|
||||
dummy, tmp_path = tempfile.mkstemp()
|
||||
|
||||
try:
|
||||
f.write(content)
|
||||
with open(tmp_path, "w") as f:
|
||||
f.write(content)
|
||||
except IOError as e:
|
||||
module.add_cleanup_file(tmp_path)
|
||||
module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, to_native(e)))
|
||||
f.close()
|
||||
module.atomic_move(tmp_path, filename)
|
||||
|
||||
|
||||
@@ -525,7 +564,7 @@ def enforce_state(module, params):
|
||||
follow = params.get('follow', False)
|
||||
error_msg = "Error getting key from: %s"
|
||||
|
||||
# if the key is a url, request it and use it as key source
|
||||
# if the key is a url or file, request it and use it as key source
|
||||
if key.startswith("http"):
|
||||
try:
|
||||
resp, info = fetch_url(module, key)
|
||||
@@ -539,6 +578,19 @@ def enforce_state(module, params):
|
||||
# resp.read gives bytes on python3, convert to native string type
|
||||
key = to_native(key, errors='surrogate_or_strict')
|
||||
|
||||
if key.startswith("file"):
|
||||
# if the key is an absolute path, check for existense and use it as a key source
|
||||
key_path = urlparse(key).path
|
||||
if not os.path.exists(key_path):
|
||||
module.fail_json(msg="Path to a key file not found: %s" % key_path)
|
||||
if not os.path.isfile(key_path):
|
||||
module.fail_json(msg="Path to a key is a directory and must be a file: %s" % key_path)
|
||||
try:
|
||||
with open(key_path, 'r') as source_fh:
|
||||
key = source_fh.read()
|
||||
except OSError as e:
|
||||
module.fail_json(msg="Failed to read key file %s : %s" % (key_path, to_native(e)))
|
||||
|
||||
# extract individual keys into an array, skipping blank lines and comments
|
||||
new_keys = [s for s in key.splitlines() if s and not s.startswith('#')]
|
||||
|
||||
@@ -631,13 +683,9 @@ def enforce_state(module, params):
|
||||
}
|
||||
params['diff'] = diff
|
||||
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, diff=diff)
|
||||
writefile(module, filename, new_content)
|
||||
if not module.check_mode:
|
||||
writefile(module, filename, new_content)
|
||||
params['changed'] = True
|
||||
else:
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False)
|
||||
|
||||
return params
|
||||
|
||||
@@ -646,11 +694,11 @@ def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
user=dict(type='str', required=True),
|
||||
key=dict(type='str', required=True),
|
||||
key=dict(type='str', required=True, no_log=False),
|
||||
path=dict(type='path'),
|
||||
manage_dir=dict(type='bool', default=True),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
key_options=dict(type='str'),
|
||||
key_options=dict(type='str', no_log=False),
|
||||
exclusive=dict(type='bool', default=False),
|
||||
comment=dict(type='str'),
|
||||
validate_certs=dict(type='bool', default=True),
|
||||
|
||||
1201
plugins/modules/firewalld.py
Normal file
1201
plugins/modules/firewalld.py
Normal file
File diff suppressed because it is too large
Load Diff
405
plugins/modules/firewalld_info.py
Normal file
405
plugins/modules/firewalld_info.py
Normal file
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2021, Hideki Saito <saito@fgrep.org>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: firewalld_info
|
||||
short_description: Gather information about firewalld
|
||||
description:
|
||||
- This module gathers information about firewalld rules.
|
||||
options:
|
||||
active_zones:
|
||||
description: Gather information about active zones.
|
||||
type: bool
|
||||
default: false
|
||||
zones:
|
||||
description:
|
||||
- Gather information about specific zones.
|
||||
- If only works if O(active_zones=false).
|
||||
required: false
|
||||
type: list
|
||||
elements: str
|
||||
requirements:
|
||||
- firewalld >= 0.2.11
|
||||
- python-firewall
|
||||
- python-dbus
|
||||
author:
|
||||
- Hideki Saito (@saito-hideki)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Gather information about active zones
|
||||
ansible.posix.firewalld_info:
|
||||
active_zones: true
|
||||
register: result
|
||||
|
||||
- name: Print default zone for debugging
|
||||
ansible.builtin.debug:
|
||||
var: result.firewalld_info.default_zone
|
||||
|
||||
- name: Gather information about specific zones
|
||||
ansible.posix.firewalld_info:
|
||||
zones:
|
||||
- public
|
||||
- external
|
||||
- internal
|
||||
register: result
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
active_zones:
|
||||
description:
|
||||
- Gather active zones only if turn it C(true).
|
||||
returned: success
|
||||
type: bool
|
||||
sample: false
|
||||
collected_zones:
|
||||
description:
|
||||
- A list of collected zones.
|
||||
returned: success
|
||||
type: list
|
||||
sample: [external, internal]
|
||||
undefined_zones:
|
||||
description:
|
||||
- A list of undefined zones in C(zones) option.
|
||||
- C(undefined_zones) will be ignored for gathering process.
|
||||
returned: success
|
||||
type: list
|
||||
sample: [foo, bar]
|
||||
firewalld_info:
|
||||
description:
|
||||
- Returns various information about firewalld configuration.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
version:
|
||||
description:
|
||||
- The version information of firewalld.
|
||||
returned: success
|
||||
type: str
|
||||
sample: 0.8.2
|
||||
default_zone:
|
||||
description:
|
||||
- The zone name of default zone.
|
||||
returned: success
|
||||
type: str
|
||||
sample: public
|
||||
zones:
|
||||
description:
|
||||
- A dict of zones to gather information.
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
zone:
|
||||
description:
|
||||
- The zone name registered in firewalld.
|
||||
returned: success
|
||||
type: complex
|
||||
sample: external
|
||||
contains:
|
||||
target:
|
||||
description:
|
||||
- A list of services in the zone.
|
||||
returned: success
|
||||
type: str
|
||||
sample: ACCEPT
|
||||
icmp_block_inversion:
|
||||
description:
|
||||
- The ICMP block inversion to block
|
||||
all ICMP requests.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: false
|
||||
interfaces:
|
||||
description:
|
||||
- A list of network interfaces.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- 'eth0'
|
||||
- 'eth1'
|
||||
sources:
|
||||
description:
|
||||
- A list of source network address.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- '172.16.30.0/24'
|
||||
- '172.16.31.0/24'
|
||||
services:
|
||||
description:
|
||||
- A list of network services.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- 'dhcp'
|
||||
- 'dns'
|
||||
- 'ssh'
|
||||
ports:
|
||||
description:
|
||||
- A list of network port with protocol.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- - "22"
|
||||
- "tcp"
|
||||
- - "80"
|
||||
- "tcp"
|
||||
protocols:
|
||||
description:
|
||||
- A list of network protocol.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- "icmp"
|
||||
- "ipv6-icmp"
|
||||
forward:
|
||||
description:
|
||||
- The network interface forwarding.
|
||||
- This parameter supports on python-firewall
|
||||
0.9.0(or later) and is not collected in earlier
|
||||
versions.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: false
|
||||
masquerade:
|
||||
description:
|
||||
- The network interface masquerading.
|
||||
returned: success
|
||||
type: bool
|
||||
sample: false
|
||||
forward_ports:
|
||||
description:
|
||||
- A list of forwarding port pair with protocol.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- "icmp"
|
||||
- "ipv6-icmp"
|
||||
source_ports:
|
||||
description:
|
||||
- A list of network source port with protocol.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- - "30000"
|
||||
- "tcp"
|
||||
- - "30001"
|
||||
- "tcp"
|
||||
icmp_blocks:
|
||||
description:
|
||||
- A list of blocking icmp protocol.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- "echo-request"
|
||||
rich_rules:
|
||||
description:
|
||||
- A list of rich language rule.
|
||||
returned: success
|
||||
type: list
|
||||
sample:
|
||||
- "rule protocol value=\"icmp\" reject"
|
||||
- "rule priority=\"32767\" reject"
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
||||
from ansible_collections.ansible.posix.plugins.module_utils.version import StrictVersion
|
||||
|
||||
|
||||
try:
|
||||
import dbus
|
||||
HAS_DBUS = True
|
||||
except ImportError:
|
||||
HAS_DBUS = False
|
||||
|
||||
try:
|
||||
import firewall.client as fw_client
|
||||
import firewall.config as fw_config
|
||||
HAS_FIREWALLD = True
|
||||
except ImportError:
|
||||
HAS_FIREWALLD = False
|
||||
|
||||
|
||||
def get_version():
|
||||
return fw_config.VERSION
|
||||
|
||||
|
||||
def get_active_zones(client):
|
||||
return client.getActiveZones().keys()
|
||||
|
||||
|
||||
def get_all_zones(client):
|
||||
return client.getZones()
|
||||
|
||||
|
||||
def get_default_zone(client):
|
||||
return client.getDefaultZone()
|
||||
|
||||
|
||||
def get_zone_settings(client, zone):
|
||||
return client.getZoneSettings(zone)
|
||||
|
||||
|
||||
def get_zone_target(zone_settings):
|
||||
return zone_settings.getTarget()
|
||||
|
||||
|
||||
def get_zone_icmp_block_inversion(zone_settings):
|
||||
return zone_settings.getIcmpBlockInversion()
|
||||
|
||||
|
||||
def get_zone_interfaces(zone_settings):
|
||||
return zone_settings.getInterfaces()
|
||||
|
||||
|
||||
def get_zone_sources(zone_settings):
|
||||
return zone_settings.getSources()
|
||||
|
||||
|
||||
def get_zone_services(zone_settings):
|
||||
return zone_settings.getServices()
|
||||
|
||||
|
||||
def get_zone_ports(zone_settings):
|
||||
return zone_settings.getPorts()
|
||||
|
||||
|
||||
def get_zone_protocols(zone_settings):
|
||||
return zone_settings.getProtocols()
|
||||
|
||||
|
||||
# This function supports python-firewall 0.9.0(or later).
|
||||
def get_zone_forward(zone_settings):
|
||||
return zone_settings.getForward()
|
||||
|
||||
|
||||
def get_zone_masquerade(zone_settings):
|
||||
return zone_settings.getMasquerade()
|
||||
|
||||
|
||||
def get_zone_forward_ports(zone_settings):
|
||||
return zone_settings.getForwardPorts()
|
||||
|
||||
|
||||
def get_zone_source_ports(zone_settings):
|
||||
return zone_settings.getSourcePorts()
|
||||
|
||||
|
||||
def get_zone_icmp_blocks(zone_settings):
|
||||
return zone_settings.getIcmpBlocks()
|
||||
|
||||
|
||||
def get_zone_rich_rules(zone_settings):
|
||||
return zone_settings.getRichRules()
|
||||
|
||||
|
||||
def main():
|
||||
module_args = dict(
|
||||
active_zones=dict(required=False, type='bool', default=False),
|
||||
zones=dict(required=False, type='list', elements='str'),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=module_args,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
firewalld_info = dict()
|
||||
result = dict(
|
||||
changed=False,
|
||||
active_zones=module.params['active_zones'],
|
||||
collected_zones=list(),
|
||||
undefined_zones=list(),
|
||||
warnings=list(),
|
||||
)
|
||||
|
||||
# Exit with failure message if requirements modules are not installed.
|
||||
if not HAS_DBUS and not HAS_FIREWALLD and HAS_RESPAWN_UTIL:
|
||||
# Only respawn the module if both libraries are missing.
|
||||
# If only one is available, then usage of the "wrong" (i.e. not the system one)
|
||||
# python interpreter is likely not the problem.
|
||||
respawn_module("firewall")
|
||||
|
||||
if not HAS_DBUS:
|
||||
module.fail_json(msg=missing_required_lib('python-dbus'))
|
||||
if not HAS_FIREWALLD:
|
||||
module.fail_json(msg=missing_required_lib('python-firewall'))
|
||||
|
||||
# If you want to show warning messages in the task running process,
|
||||
# you can append the message to the 'warn' list.
|
||||
warn = list()
|
||||
|
||||
try:
|
||||
client = fw_client.FirewallClient()
|
||||
|
||||
# Gather general information of firewalld.
|
||||
firewalld_info['version'] = get_version()
|
||||
firewalld_info['default_zone'] = get_default_zone(client)
|
||||
|
||||
# Gather information for zones.
|
||||
zones_info = dict()
|
||||
collect_zones = list()
|
||||
ignore_zones = list()
|
||||
if module.params['active_zones']:
|
||||
collect_zones = get_active_zones(client)
|
||||
elif module.params['zones']:
|
||||
all_zones = get_all_zones(client)
|
||||
specified_zones = module.params['zones']
|
||||
collect_zones = list(set(specified_zones) & set(all_zones))
|
||||
ignore_zones = list(set(specified_zones) - set(collect_zones))
|
||||
if ignore_zones:
|
||||
warn.append(
|
||||
'Please note: zone:(%s) have been ignored in the gathering process.' % ','.join(ignore_zones))
|
||||
else:
|
||||
collect_zones = get_all_zones(client)
|
||||
|
||||
for zone in collect_zones:
|
||||
# Gather settings for each zone based on the output of
|
||||
# 'firewall-cmd --info-zone=<ZONE>' command.
|
||||
zone_info = dict()
|
||||
zone_settings = get_zone_settings(client, zone)
|
||||
zone_info['target'] = get_zone_target(zone_settings)
|
||||
zone_info['icmp_block_inversion'] = get_zone_icmp_block_inversion(zone_settings)
|
||||
zone_info['interfaces'] = get_zone_interfaces(zone_settings)
|
||||
zone_info['sources'] = get_zone_sources(zone_settings)
|
||||
zone_info['services'] = get_zone_services(zone_settings)
|
||||
zone_info['ports'] = get_zone_ports(zone_settings)
|
||||
zone_info['protocols'] = get_zone_protocols(zone_settings)
|
||||
zone_info['masquerade'] = get_zone_masquerade(zone_settings)
|
||||
zone_info['forward_ports'] = get_zone_forward_ports(zone_settings)
|
||||
zone_info['source_ports'] = get_zone_source_ports(zone_settings)
|
||||
zone_info['icmp_blocks'] = get_zone_icmp_blocks(zone_settings)
|
||||
zone_info['rich_rules'] = get_zone_rich_rules(zone_settings)
|
||||
|
||||
# The 'forward' parameter supports on python-firewall 0.9.0(or later).
|
||||
if StrictVersion(firewalld_info['version']) >= StrictVersion('0.9.0'):
|
||||
zone_info['forward'] = get_zone_forward(zone_settings)
|
||||
|
||||
zones_info[zone] = zone_info
|
||||
firewalld_info['zones'] = zones_info
|
||||
except AttributeError as e:
|
||||
module.fail_json(msg=('firewalld probably not be running, Or the following method '
|
||||
'is not supported with your python-firewall version. (Error: %s)') % to_native(e))
|
||||
except dbus.exceptions.DBusException as e:
|
||||
module.fail_json(msg=('Unable to gather firewalld settings.'
|
||||
' You may need to run as the root user or'
|
||||
' use become. (Error: %s)' % to_native(e)))
|
||||
|
||||
result['collected_zones'] = collect_zones
|
||||
result['undefined_zones'] = ignore_zones
|
||||
result['firewalld_info'] = firewalld_info
|
||||
result['warnings'] = warn
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -19,67 +19,90 @@ description:
|
||||
author:
|
||||
- Ansible Core Team
|
||||
- Seth Vidal (@skvidal)
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
path:
|
||||
description:
|
||||
- Path to the mount point (e.g. C(/mnt/files)).
|
||||
- Before Ansible 2.3 this option was only usable as I(dest), I(destfile) and I(name).
|
||||
- Before Ansible 2.3 this option was only usable as O(ignore:dest), O(ignore:destfile), and O(name).
|
||||
type: path
|
||||
required: true
|
||||
aliases: [ name ]
|
||||
src:
|
||||
description:
|
||||
- Device (or NFS volume, or something else) to be mounted on I(path).
|
||||
- Required when I(state) set to C(present) or C(mounted).
|
||||
- Required when O(state) set to V(present), V(mounted), or V(ephemeral).
|
||||
- Ignored when O(state) set to V(absent) or V(unmounted).
|
||||
type: path
|
||||
fstype:
|
||||
description:
|
||||
- Filesystem type.
|
||||
- Required when I(state) is C(present) or C(mounted).
|
||||
- Required when O(state) is V(present), V(mounted), or V(ephemeral).
|
||||
type: str
|
||||
opts:
|
||||
description:
|
||||
- Mount options (see fstab(5), or vfstab(4) on Solaris).
|
||||
type: str
|
||||
opts_no_log:
|
||||
description:
|
||||
- Do not log opts.
|
||||
type: bool
|
||||
default: false
|
||||
dump:
|
||||
description:
|
||||
- Dump (see fstab(5)).
|
||||
- Note that if set to C(null) and I(state) set to C(present),
|
||||
- Note that if set to C(null) and O(state=present),
|
||||
it will cease to work and duplicate entries will be made
|
||||
with subsequent runs.
|
||||
- Has no effect on Solaris systems.
|
||||
- Has no effect on Solaris systems or when used with O(state=ephemeral).
|
||||
type: str
|
||||
default: 0
|
||||
default: '0'
|
||||
passno:
|
||||
description:
|
||||
- Passno (see fstab(5)).
|
||||
- Note that if set to C(null) and I(state) set to C(present),
|
||||
- Note that if set to C(null) and O(state=present),
|
||||
it will cease to work and duplicate entries will be made
|
||||
with subsequent runs.
|
||||
- Deprecated on Solaris systems.
|
||||
- Deprecated on Solaris systems. Has no effect when used with O(state=ephemeral).
|
||||
type: str
|
||||
default: 0
|
||||
default: '0'
|
||||
state:
|
||||
description:
|
||||
- If C(mounted), the device will be actively mounted and appropriately
|
||||
- If V(mounted), the device will be actively mounted and appropriately
|
||||
configured in I(fstab). If the mount point is not present, the mount
|
||||
point will be created.
|
||||
- If C(unmounted), the device will be unmounted without changing I(fstab).
|
||||
- C(present) only specifies that the device is to be configured in
|
||||
- If V(unmounted), the device will be unmounted without changing I(fstab).
|
||||
- V(present) only specifies that the device is to be configured in
|
||||
I(fstab) and does not trigger or require a mount.
|
||||
- C(absent) specifies that the device mount's entry will be removed from
|
||||
I(fstab) and will also unmount the device and remove the mount
|
||||
point.
|
||||
- C(remounted) specifies that the device will be remounted for when you
|
||||
- V(ephemeral) only specifies that the device is to be mounted, without changing
|
||||
I(fstab). If it is already mounted, a remount will be triggered.
|
||||
This will always return RV(ignore:changed=true). If the mount point O(path)
|
||||
has already a device mounted on, and its source is different than O(src),
|
||||
the module will fail to avoid unexpected unmount or mount point override.
|
||||
If the mount point is not present, the mount point will be created.
|
||||
The I(fstab) is completely ignored. This option is added in version 1.5.0.
|
||||
- V(absent) specifies that the mount point entry O(path) will be removed
|
||||
from I(fstab) and will also unmount the mounted device and remove the
|
||||
mount point. A mounted device will be unmounted regardless of O(src) or its
|
||||
real source. V(absent) does not unmount recursively, and the module will
|
||||
fail if multiple devices are mounted on the same mount point. Using
|
||||
V(absent) with a mount point that is not registered in the I(fstab) has
|
||||
no effect, use V(unmounted) instead.
|
||||
- V(remounted) specifies that the device will be remounted for when you
|
||||
want to force a refresh on the mount itself (added in 2.9). This will
|
||||
always return changed=true. If I(opts) is set, the options will be
|
||||
always return RV(ignore:changed=true). If O(opts) is set, the options will be
|
||||
applied to the remount, but will not change I(fstab). Additionally,
|
||||
if I(opts) is set, and the remount command fails, the module will
|
||||
error to prevent unexpected mount changes. Try using C(mounted)
|
||||
instead to work around this issue.
|
||||
if O(opts) is set, and the remount command fails, the module will
|
||||
error to prevent unexpected mount changes. Try using V(mounted)
|
||||
instead to work around this issue. V(remounted) expects the mount point
|
||||
to be present in the I(fstab). To remount a mount point not registered
|
||||
in I(fstab), use V(ephemeral) instead, especially with BSD nodes.
|
||||
- V(absent_from_fstab) specifies that the device mount's entry will be
|
||||
removed from I(fstab). This option does not unmount it or delete the
|
||||
mountpoint.
|
||||
type: str
|
||||
required: true
|
||||
choices: [ absent, mounted, present, unmounted, remounted ]
|
||||
choices: [ absent, absent_from_fstab, mounted, present, unmounted, remounted, ephemeral ]
|
||||
fstab:
|
||||
description:
|
||||
- File to use instead of C(/etc/fstab).
|
||||
@@ -87,24 +110,32 @@ options:
|
||||
- This might be useful if you need to configure mountpoints in a chroot environment.
|
||||
- OpenBSD does not allow specifying alternate fstab files with mount so do not
|
||||
use this on OpenBSD with any state that operates on the live filesystem.
|
||||
- This parameter defaults to /etc/fstab or /etc/vfstab on Solaris.
|
||||
- This parameter defaults to C(/etc/fstab) or C(/etc/vfstab) on Solaris.
|
||||
- This parameter is ignored when O(state=ephemeral).
|
||||
type: str
|
||||
boot:
|
||||
description:
|
||||
- Determines if the filesystem should be mounted on boot.
|
||||
- Only applies to Solaris systems.
|
||||
- Only applies to Solaris and Linux systems.
|
||||
- For Solaris systems, C(true) will set C(yes) as the value of mount at boot
|
||||
in C(/etc/vfstab).
|
||||
- For Linux, FreeBSD, NetBSD and OpenBSD systems, C(false) will add C(noauto)
|
||||
to mount options in C(/etc/fstab).
|
||||
- To avoid mount option conflicts, if C(noauto) specified in O(opts),
|
||||
mount module will ignore O(boot).
|
||||
- This parameter is ignored when O(state=ephemeral).
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
backup:
|
||||
description:
|
||||
- Create a backup file including the timestamp information so you can get
|
||||
the original file back if you somehow clobbered it incorrectly.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
notes:
|
||||
- As of Ansible 2.3, the I(name) option has been changed to I(path) as
|
||||
default, but I(name) still works as well.
|
||||
- Using C(remounted) with I(opts) set may create unexpected results based on
|
||||
- As of Ansible 2.3, the O(name) option has been changed to O(path) as
|
||||
default, but O(name) still works as well.
|
||||
- Using O(state=remounted) with O(opts) set may create unexpected results based on
|
||||
the existing options already defined on mount, so care should be taken to
|
||||
ensure that conflicting options are not present before hand.
|
||||
'''
|
||||
@@ -165,25 +196,46 @@ EXAMPLES = r'''
|
||||
ansible.posix.mount:
|
||||
src: 192.168.1.100:/nfs/ssd/shared_data
|
||||
path: /mnt/shared_data
|
||||
opts: rw,sync,hard,intr
|
||||
opts: rw,sync,hard
|
||||
state: mounted
|
||||
fstype: nfs
|
||||
'''
|
||||
|
||||
- name: Mount NFS volumes with noauto according to boot option
|
||||
ansible.posix.mount:
|
||||
src: 192.168.1.100:/nfs/ssd/shared_data
|
||||
path: /mnt/shared_data
|
||||
opts: rw,sync,hard
|
||||
boot: false
|
||||
state: mounted
|
||||
fstype: nfs
|
||||
|
||||
- name: Mount ephemeral SMB volume
|
||||
ansible.posix.mount:
|
||||
src: //192.168.1.200/share
|
||||
path: /mnt/smb_share
|
||||
opts: "rw,vers=3,file_mode=0600,dir_mode=0700,dom={{ ad_domain }},username={{ ad_username }},password={{ ad_password }}"
|
||||
opts_no_log: true
|
||||
fstype: cifs
|
||||
state: ephemeral
|
||||
'''
|
||||
|
||||
import errno
|
||||
import os
|
||||
import platform
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.posix.plugins.module_utils.ismount import ismount
|
||||
from ansible_collections.ansible.posix.plugins.module_utils.mount import ismount
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_bytes, to_native
|
||||
from ansible.module_utils.parsing.convert_bool import boolean
|
||||
|
||||
|
||||
def write_fstab(module, lines, path):
|
||||
|
||||
if module.params['backup']:
|
||||
module.backup_local(path)
|
||||
backup_file = module.backup_local(path)
|
||||
else:
|
||||
backup_file = ""
|
||||
|
||||
fs_w = open(path, 'w')
|
||||
|
||||
@@ -193,6 +245,8 @@ def write_fstab(module, lines, path):
|
||||
fs_w.flush()
|
||||
fs_w.close()
|
||||
|
||||
return backup_file
|
||||
|
||||
|
||||
def _escape_fstab(v):
|
||||
"""Escape invalid characters in fstab fields.
|
||||
@@ -205,7 +259,7 @@ def _escape_fstab(v):
|
||||
if isinstance(v, int):
|
||||
return v
|
||||
else:
|
||||
return(
|
||||
return (
|
||||
v.
|
||||
replace('\\', '\\134').
|
||||
replace(' ', '\\040').
|
||||
@@ -225,7 +279,7 @@ def _set_mount_save_old(module, args):
|
||||
old_lines = []
|
||||
exists = False
|
||||
changed = False
|
||||
escaped_args = dict([(k, _escape_fstab(v)) for k, v in iteritems(args)])
|
||||
escaped_args = dict([(k, _escape_fstab(v)) for k, v in iteritems(args) if k != 'warnings'])
|
||||
new_line = '%(src)s %(name)s %(fstype)s %(opts)s %(dump)s %(passno)s\n'
|
||||
|
||||
if platform.system() == 'SunOS':
|
||||
@@ -233,6 +287,10 @@ def _set_mount_save_old(module, args):
|
||||
'%(src)s - %(name)s %(fstype)s %(passno)s %(boot)s %(opts)s\n')
|
||||
|
||||
for line in open(args['fstab'], 'r').readlines():
|
||||
# Append newline if the line in fstab does not finished with newline.
|
||||
if not line.endswith('\n'):
|
||||
line += '\n'
|
||||
|
||||
old_lines.append(line)
|
||||
|
||||
if not line.strip():
|
||||
@@ -245,7 +303,7 @@ def _set_mount_save_old(module, args):
|
||||
|
||||
continue
|
||||
|
||||
fields = line.split()
|
||||
fields = line.split('#')[0].split()
|
||||
|
||||
# Check if we got a valid line for splitting
|
||||
# (on Linux the 5th and the 6th field is optional)
|
||||
@@ -315,7 +373,7 @@ def _set_mount_save_old(module, args):
|
||||
changed = True
|
||||
|
||||
if changed and not module.check_mode:
|
||||
write_fstab(module, to_write, args['fstab'])
|
||||
args['backup_file'] = write_fstab(module, to_write, args['fstab'])
|
||||
|
||||
return (args['name'], old_lines, changed)
|
||||
|
||||
@@ -405,6 +463,24 @@ def _set_fstab_args(fstab_file):
|
||||
return result
|
||||
|
||||
|
||||
def _set_ephemeral_args(args):
|
||||
result = []
|
||||
# Set fstype switch according to platform. SunOS/Solaris use -F
|
||||
if platform.system().lower() == 'sunos':
|
||||
result.append('-F')
|
||||
else:
|
||||
result.append('-t')
|
||||
result.append(args['fstype'])
|
||||
|
||||
# Even if '-o remount' is already set, specifying multiple -o is valid
|
||||
if args['opts'] != 'defaults':
|
||||
result += ['-o', args['opts']]
|
||||
|
||||
result.append(args['src'])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def mount(module, args):
|
||||
"""Mount up a path or remount if needed."""
|
||||
|
||||
@@ -421,7 +497,11 @@ def mount(module, args):
|
||||
'OpenBSD does not support alternate fstab files. Do not '
|
||||
'specify the fstab parameter for OpenBSD hosts'))
|
||||
else:
|
||||
cmd += _set_fstab_args(args['fstab'])
|
||||
if module.params['state'] != 'ephemeral':
|
||||
cmd += _set_fstab_args(args['fstab'])
|
||||
|
||||
if module.params['state'] == 'ephemeral':
|
||||
cmd += _set_ephemeral_args(args)
|
||||
|
||||
cmd += [name]
|
||||
|
||||
@@ -473,18 +553,24 @@ def remount(module, args):
|
||||
'OpenBSD does not support alternate fstab files. Do not '
|
||||
'specify the fstab parameter for OpenBSD hosts'))
|
||||
else:
|
||||
cmd += _set_fstab_args(args['fstab'])
|
||||
if module.params['state'] != 'ephemeral':
|
||||
cmd += _set_fstab_args(args['fstab'])
|
||||
|
||||
if module.params['state'] == 'ephemeral':
|
||||
cmd += _set_ephemeral_args(args)
|
||||
|
||||
cmd += [args['name']]
|
||||
out = err = ''
|
||||
|
||||
try:
|
||||
if platform.system().lower().endswith('bsd'):
|
||||
if module.params['state'] != 'ephemeral' and platform.system().lower().endswith('bsd'):
|
||||
# Note: Forcing BSDs to do umount/mount due to BSD remount not
|
||||
# working as expected (suspect bug in the BSD mount command)
|
||||
# Interested contributor could rework this to use mount options on
|
||||
# the CLI instead of relying on fstab
|
||||
# https://github.com/ansible/ansible-modules-core/issues/5591
|
||||
# Note: this does not affect ephemeral state as all options
|
||||
# are set on the CLI and fstab is expected to be ignored.
|
||||
rc = 1
|
||||
else:
|
||||
rc, out, err = module.run_command(cmd)
|
||||
@@ -638,27 +724,73 @@ def get_linux_mounts(module, mntinfo_file="/proc/self/mountinfo"):
|
||||
return mounts
|
||||
|
||||
|
||||
def _is_same_mount_src(module, src, mountpoint, linux_mounts):
|
||||
"""Return True if the mounted fs on mountpoint is the same source than src. Return False if mountpoint is not a mountpoint"""
|
||||
# If the provided mountpoint is not a mountpoint, don't waste time
|
||||
if (
|
||||
not ismount(mountpoint) and
|
||||
not is_bind_mounted(module, linux_mounts, mountpoint)):
|
||||
return False
|
||||
|
||||
# Treat Linux bind mounts
|
||||
if platform.system() == 'Linux' and linux_mounts is not None:
|
||||
# For Linux bind mounts only: the mount command does not return
|
||||
# the actual source for bind mounts, but the device of the source.
|
||||
# is_bind_mounted() called with the 'src' parameter will return True if
|
||||
# the mountpoint is a bind mount AND the source FS is the same than 'src'.
|
||||
# is_bind_mounted() is not reliable on Solaris, NetBSD and OpenBSD.
|
||||
# But we can rely on 'mount -v' on all other platforms, and Linux non-bind mounts.
|
||||
if is_bind_mounted(module, linux_mounts, mountpoint, src):
|
||||
return True
|
||||
|
||||
# mount with parameter -v has a close behavior on Linux, *BSD, SunOS
|
||||
# Requires -v with SunOS. Without -v, source and destination are reversed
|
||||
# Output format differs from a system to another, but field[0:3] are consistent: [src, 'on', dest]
|
||||
cmd = '%s -v' % module.get_bin_path('mount', required=True)
|
||||
rc, out, err = module.run_command(cmd)
|
||||
mounts = []
|
||||
|
||||
if len(out):
|
||||
mounts = to_native(out).strip().split('\n')
|
||||
else:
|
||||
module.fail_json(msg="Unable to retrieve mount info with command '%s'" % cmd)
|
||||
|
||||
for mnt in mounts:
|
||||
fields = mnt.split()
|
||||
mp_src = fields[0]
|
||||
mp_dst = fields[2]
|
||||
if mp_src == src and mp_dst == mountpoint:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
boot=dict(type='bool', default=True),
|
||||
dump=dict(type='str'),
|
||||
dump=dict(type='str', default='0'),
|
||||
fstab=dict(type='str'),
|
||||
fstype=dict(type='str'),
|
||||
path=dict(type='path', required=True, aliases=['name']),
|
||||
opts=dict(type='str'),
|
||||
passno=dict(type='str'),
|
||||
opts_no_log=dict(type='bool', default=False),
|
||||
passno=dict(type='str', no_log=False, default='0'),
|
||||
src=dict(type='path'),
|
||||
backup=dict(type='bool', default=False),
|
||||
state=dict(type='str', required=True, choices=['absent', 'mounted', 'present', 'unmounted', 'remounted']),
|
||||
state=dict(type='str', required=True, choices=['absent', 'absent_from_fstab', 'mounted', 'present', 'unmounted', 'remounted', 'ephemeral']),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=(
|
||||
['state', 'mounted', ['src', 'fstype']],
|
||||
['state', 'present', ['src', 'fstype']],
|
||||
['state', 'ephemeral', ['src', 'fstype']]
|
||||
),
|
||||
)
|
||||
|
||||
if module.params['opts_no_log']:
|
||||
module.no_log_values.add(module.params['opts'])
|
||||
|
||||
# solaris args:
|
||||
# name, src, fstype, opts, boot, passno, state, fstab=/etc/vfstab
|
||||
# linux args:
|
||||
@@ -671,7 +803,8 @@ def main():
|
||||
opts='-',
|
||||
passno='-',
|
||||
fstab=module.params['fstab'],
|
||||
boot='yes'
|
||||
boot='yes' if module.params['boot'] else 'no',
|
||||
warnings=[]
|
||||
)
|
||||
if args['fstab'] is None:
|
||||
args['fstab'] = '/etc/vfstab'
|
||||
@@ -681,7 +814,9 @@ def main():
|
||||
opts='defaults',
|
||||
dump='0',
|
||||
passno='0',
|
||||
fstab=module.params['fstab']
|
||||
fstab=module.params['fstab'],
|
||||
boot='yes',
|
||||
warnings=[]
|
||||
)
|
||||
if args['fstab'] is None:
|
||||
args['fstab'] = '/etc/fstab'
|
||||
@@ -690,6 +825,7 @@ def main():
|
||||
if platform.system() == 'FreeBSD':
|
||||
args['opts'] = 'rw'
|
||||
|
||||
args['backup_file'] = ""
|
||||
linux_mounts = []
|
||||
|
||||
# Cache all mounts here in order we have consistent results if we need to
|
||||
@@ -698,26 +834,38 @@ def main():
|
||||
linux_mounts = get_linux_mounts(module)
|
||||
|
||||
if linux_mounts is None:
|
||||
args['warnings'] = (
|
||||
'Cannot open file /proc/self/mountinfo. '
|
||||
'Bind mounts might be misinterpreted.')
|
||||
args['warnings'].append('Cannot open file /proc/self/mountinfo.'
|
||||
' Bind mounts might be misinterpreted.')
|
||||
|
||||
# Override defaults with user specified params
|
||||
for key in ('src', 'fstype', 'passno', 'opts', 'dump', 'fstab'):
|
||||
if module.params[key] is not None:
|
||||
args[key] = module.params[key]
|
||||
if platform.system().lower() == 'linux' or platform.system().lower().endswith('bsd'):
|
||||
# Linux, FreeBSD, NetBSD and OpenBSD have 'noauto' as mount option to
|
||||
# handle mount on boot. To avoid mount option conflicts, if 'noauto'
|
||||
# specified in 'opts', mount module will ignore 'boot'.
|
||||
opts = args['opts'].split(',')
|
||||
if module.params['boot'] and 'noauto' in opts:
|
||||
args['warnings'].append("Ignore the 'boot' due to 'opts' contains 'noauto'.")
|
||||
elif not module.params['boot']:
|
||||
args['boot'] = 'no'
|
||||
opts.append('noauto')
|
||||
args['opts'] = ','.join(opts)
|
||||
|
||||
# If fstab file does not exist, we first need to create it. This mainly
|
||||
# happens when fstab option is passed to the module.
|
||||
if not os.path.exists(args['fstab']):
|
||||
if not os.path.exists(os.path.dirname(args['fstab'])):
|
||||
os.makedirs(os.path.dirname(args['fstab']))
|
||||
try:
|
||||
open(args['fstab'], 'a').close()
|
||||
except PermissionError as e:
|
||||
module.fail_json(msg="Failed to open %s due to permission issue" % args['fstab'])
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Failed to open %s due to %s" % (args['fstab'], to_native(e)))
|
||||
# If state is 'ephemeral', we do not need fstab file
|
||||
if module.params['state'] != 'ephemeral':
|
||||
if not os.path.exists(args['fstab']):
|
||||
if not os.path.exists(os.path.dirname(args['fstab'])):
|
||||
os.makedirs(os.path.dirname(args['fstab']))
|
||||
try:
|
||||
open(args['fstab'], 'a').close()
|
||||
except PermissionError as e:
|
||||
module.fail_json(msg="Failed to open %s due to permission issue" % args['fstab'])
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Failed to open %s due to %s" % (args['fstab'], to_native(e)))
|
||||
|
||||
# absent:
|
||||
# Remove from fstab and unmounted.
|
||||
@@ -728,12 +876,16 @@ def main():
|
||||
# mounted:
|
||||
# Add to fstab if not there and make sure it is mounted. If it has
|
||||
# changed in fstab then remount it.
|
||||
# ephemeral:
|
||||
# Do not change fstab state, but mount.
|
||||
|
||||
state = module.params['state']
|
||||
name = module.params['path']
|
||||
changed = False
|
||||
|
||||
if state == 'absent':
|
||||
if state == 'absent_from_fstab':
|
||||
name, changed = unset_mount(module, args)
|
||||
elif state == 'absent':
|
||||
name, changed = unset_mount(module, args)
|
||||
|
||||
if changed and not module.check_mode:
|
||||
@@ -759,7 +911,7 @@ def main():
|
||||
msg="Error unmounting %s: %s" % (name, msg))
|
||||
|
||||
changed = True
|
||||
elif state == 'mounted':
|
||||
elif state == 'mounted' or state == 'ephemeral':
|
||||
dirs_created = []
|
||||
if not os.path.exists(name) and not module.check_mode:
|
||||
try:
|
||||
@@ -787,7 +939,11 @@ def main():
|
||||
module.fail_json(
|
||||
msg="Error making dir %s: %s" % (name, to_native(e)))
|
||||
|
||||
name, backup_lines, changed = _set_mount_save_old(module, args)
|
||||
# ephemeral: completely ignore fstab
|
||||
if state != 'ephemeral':
|
||||
name, backup_lines, changed = _set_mount_save_old(module, args)
|
||||
else:
|
||||
name, backup_lines, changed = args['name'], [], False
|
||||
res = 0
|
||||
|
||||
if (
|
||||
@@ -797,7 +953,26 @@ def main():
|
||||
if changed and not module.check_mode:
|
||||
res, msg = remount(module, args)
|
||||
changed = True
|
||||
|
||||
# When 'state' == 'ephemeral', we don't know what is in fstab, and 'changed' is always False
|
||||
if state == 'ephemeral':
|
||||
# If state == 'ephemeral', check if the mountpoint src == module.params['src']
|
||||
# If it doesn't, fail to prevent unwanted unmount or unwanted mountpoint override
|
||||
if _is_same_mount_src(module, args['src'], args['name'], linux_mounts):
|
||||
changed = True
|
||||
if not module.check_mode:
|
||||
res, msg = remount(module, args)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg=(
|
||||
'Ephemeral mount point is already mounted with a different '
|
||||
'source than the specified one. Failing in order to prevent an '
|
||||
'unwanted unmount or override operation. Try replacing this command with '
|
||||
'a "state: unmounted" followed by a "state: ephemeral", or use '
|
||||
'a different destination path.'))
|
||||
|
||||
else:
|
||||
# If not already mounted, mount it
|
||||
changed = True
|
||||
|
||||
if not module.check_mode:
|
||||
@@ -809,7 +984,8 @@ def main():
|
||||
# A non-working fstab entry may break the system at the reboot,
|
||||
# so undo all the changes if possible.
|
||||
try:
|
||||
write_fstab(module, backup_lines, args['fstab'])
|
||||
if state != 'ephemeral':
|
||||
write_fstab(module, backup_lines, args['fstab'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -833,6 +1009,10 @@ def main():
|
||||
else:
|
||||
module.fail_json(msg='Unexpected position reached')
|
||||
|
||||
# If the managed node is Solaris, convert the boot value type to Boolean
|
||||
# to match the type of return value with the module argument.
|
||||
if platform.system().lower() == 'sunos':
|
||||
args['boot'] = boolean(args['boot'])
|
||||
module.exit_json(changed=changed, **args)
|
||||
|
||||
|
||||
|
||||
@@ -19,11 +19,12 @@ author:
|
||||
description:
|
||||
- Apply patch files using the GNU patch tool.
|
||||
short_description: Apply patch files using the GNU patch tool
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
basedir:
|
||||
description:
|
||||
- Path of a base directory in which the patch file will be applied.
|
||||
- May be omitted when C(dest) option is specified, otherwise required.
|
||||
- May be omitted when O(dest) option is specified, otherwise required.
|
||||
type: path
|
||||
dest:
|
||||
description:
|
||||
@@ -36,7 +37,7 @@ options:
|
||||
src:
|
||||
description:
|
||||
- Path of the patch file as accepted by the GNU patch tool. If
|
||||
C(remote_src) is 'no', the patch source file is looked up from the
|
||||
O(remote_src=false), the patch source file is looked up from the
|
||||
module's I(files) directory.
|
||||
type: path
|
||||
required: true
|
||||
@@ -49,10 +50,10 @@ options:
|
||||
default: present
|
||||
remote_src:
|
||||
description:
|
||||
- If C(no), it will search for src at originating/master machine, if C(yes) it will
|
||||
go to the remote/target machine for the C(src).
|
||||
- If V(false), it will search for src at originating/controller machine,
|
||||
- If C(true), it will go to the remote/target machine for the O(src).
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
strip:
|
||||
description:
|
||||
- Number that indicates the smallest prefix containing leading slashes
|
||||
@@ -64,15 +65,20 @@ options:
|
||||
description:
|
||||
- Passes C(--backup --version-control=numbered) to patch, producing numbered backup copies.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
binary:
|
||||
description:
|
||||
- Setting to C(yes) will disable patch's heuristic for transforming CRLF
|
||||
- Setting to V(true) will disable patch's heuristic for transforming CRLF
|
||||
line endings into LF.
|
||||
- Line endings of src and dest must match.
|
||||
- If set to C(no), C(patch) will replace CRLF in C(src) files on POSIX.
|
||||
- Line endings of O(src) and O(dest) must match.
|
||||
- If set to V(false), C(patch) will replace CRLF in O(src) files on POSIX.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
ignore_whitespace:
|
||||
description:
|
||||
- Setting to V(true) will ignore white space changes between patch and input.
|
||||
type: bool
|
||||
default: false
|
||||
notes:
|
||||
- This module requires GNU I(patch) utility to be installed on the remote host.
|
||||
'''
|
||||
@@ -115,23 +121,25 @@ def add_dry_run_option(opts):
|
||||
opts.append('--dry-run')
|
||||
|
||||
|
||||
def is_already_applied(patch_func, patch_file, basedir, dest_file=None, binary=False, strip=0, state='present'):
|
||||
def is_already_applied(patch_func, patch_file, basedir, dest_file=None, binary=False, ignore_whitespace=False, strip=0, state='present'):
|
||||
opts = ['--quiet', '--forward',
|
||||
"--strip=%s" % strip, "--directory='%s'" % basedir,
|
||||
"--input='%s'" % patch_file]
|
||||
add_dry_run_option(opts)
|
||||
if binary:
|
||||
opts.append('--binary')
|
||||
if ignore_whitespace:
|
||||
opts.append('--ignore-whitespace')
|
||||
if dest_file:
|
||||
opts.append("'%s'" % dest_file)
|
||||
if state == 'present':
|
||||
opts.append('--reverse')
|
||||
|
||||
(rc, _, _) = patch_func(opts)
|
||||
(rc, var1, var2) = patch_func(opts)
|
||||
return rc == 0
|
||||
|
||||
|
||||
def apply_patch(patch_func, patch_file, basedir, dest_file=None, binary=False, strip=0, dry_run=False, backup=False, state='present'):
|
||||
def apply_patch(patch_func, patch_file, basedir, dest_file=None, binary=False, ignore_whitespace=False, strip=0, dry_run=False, backup=False, state='present'):
|
||||
opts = ['--quiet', '--forward', '--batch', '--reject-file=-',
|
||||
"--strip=%s" % strip, "--directory='%s'" % basedir,
|
||||
"--input='%s'" % patch_file]
|
||||
@@ -139,6 +147,8 @@ def apply_patch(patch_func, patch_file, basedir, dest_file=None, binary=False, s
|
||||
add_dry_run_option(opts)
|
||||
if binary:
|
||||
opts.append('--binary')
|
||||
if ignore_whitespace:
|
||||
opts.append('--ignore-whitespace')
|
||||
if dest_file:
|
||||
opts.append("'%s'" % dest_file)
|
||||
if backup:
|
||||
@@ -164,6 +174,7 @@ def main():
|
||||
# since patch will create numbered copies, not strftime("%Y-%m-%d@%H:%M:%S~")
|
||||
backup=dict(type='bool', default=False),
|
||||
binary=dict(type='bool', default=False),
|
||||
ignore_whitespace=dict(type='bool', default=False),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
),
|
||||
required_one_of=[['dest', 'basedir']],
|
||||
@@ -196,9 +207,10 @@ def main():
|
||||
p.src = os.path.abspath(p.src)
|
||||
|
||||
changed = False
|
||||
if not is_already_applied(patch_func, p.src, p.basedir, dest_file=p.dest, binary=p.binary, strip=p.strip, state=p.state):
|
||||
if not is_already_applied(patch_func, p.src, p.basedir, dest_file=p.dest, binary=p.binary,
|
||||
ignore_whitespace=p.ignore_whitespace, strip=p.strip, state=p.state):
|
||||
try:
|
||||
apply_patch(patch_func, p.src, p.basedir, dest_file=p.dest, binary=p.binary, strip=p.strip,
|
||||
apply_patch(patch_func, p.src, p.basedir, dest_file=p.dest, binary=p.binary, ignore_whitespace=p.ignore_whitespace, strip=p.strip,
|
||||
dry_run=module.check_mode, backup=p.backup, state=p.state)
|
||||
changed = True
|
||||
except PatchError as e:
|
||||
|
||||
76
plugins/modules/rhel_facts.py
Normal file
76
plugins/modules/rhel_facts.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: Red Hat Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rhel_facts
|
||||
version_added: 1.5.0
|
||||
short_description: Facts module to set or override RHEL specific facts.
|
||||
description:
|
||||
- Compatibility layer for using the M(ansible.builtin.package) module for rpm-ostree based systems via setting the C(pkg_mgr) fact correctly.
|
||||
author:
|
||||
- Adam Miller (@maxamillion)
|
||||
requirements:
|
||||
- rpm-ostree
|
||||
seealso:
|
||||
- module: ansible.builtin.package
|
||||
options: {}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Playbook to use the package module on all RHEL footprints
|
||||
vars:
|
||||
ansible_facts_modules:
|
||||
- setup # REQUIRED to be run before all custom fact modules
|
||||
- ansible.posix.rhel_facts
|
||||
tasks:
|
||||
- name: Ensure packages are installed
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- htop
|
||||
- ansible
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = """
|
||||
ansible_facts:
|
||||
description: Relevant Ansible Facts
|
||||
returned: when needed
|
||||
type: complex
|
||||
contains:
|
||||
pkg_mgr:
|
||||
description: System-level package manager override
|
||||
returned: when needed
|
||||
type: str
|
||||
sample: {'pkg_mgr': 'ansible.posix.rhel_facts'}
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
ansible_facts = {}
|
||||
|
||||
# Verify that the platform is an rpm-ostree based system
|
||||
if os.path.exists("/run/ostree-booted"):
|
||||
ansible_facts['pkg_mgr'] = 'ansible.posix.rhel_rpm_ostree'
|
||||
|
||||
module.exit_json(ansible_facts=ansible_facts, changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
124
plugins/modules/rhel_rpm_ostree.py
Normal file
124
plugins/modules/rhel_rpm_ostree.py
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: Red Hat Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rhel_rpm_ostree
|
||||
version_added: 1.5.0
|
||||
short_description: Ensure packages exist in a RHEL for Edge rpm-ostree based system
|
||||
description:
|
||||
- Compatibility layer for using the "package" module for RHEL for Edge systems utilizing the RHEL System Roles.
|
||||
author:
|
||||
- Adam Miller (@maxamillion)
|
||||
requirements:
|
||||
- rpm-ostree
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- A package name or package specifier with version, like V(name-1.0).
|
||||
- Comparison operators for package version are valid here C(>), C(<), C(>=), C(<=). Example - C(name>=1.0).
|
||||
- If a previous version is specified, the task also needs to turn C(allow_downgrade) on.
|
||||
See the C(allow_downgrade) documentation for caveats with downgrading packages.
|
||||
- When using O(state=latest), this can be V('*') which means run C(yum -y update).
|
||||
- You can also pass a url or a local path to a rpm file (using O(state=present)).
|
||||
To operate on several packages this can accept a comma separated string of packages or (as of 2.0) a list of packages.
|
||||
aliases: [ pkg ]
|
||||
type: list
|
||||
elements: str
|
||||
default: []
|
||||
state:
|
||||
description:
|
||||
- Whether to install (V(present) or V(installed), V(latest)), or remove (V(absent) or V(removed)) a package.
|
||||
- V(present) and V(installed) will simply ensure that a desired package is installed.
|
||||
- V(latest) will update the specified package if it's not of the latest available version.
|
||||
- V(absent) and V(removed) will remove the specified package.
|
||||
- Default is C(null), however in effect the default action is V(present) unless the C(autoremove) option is
|
||||
enabled for this module, then V(absent) is inferred.
|
||||
type: str
|
||||
choices: [ absent, installed, latest, present, removed ]
|
||||
notes:
|
||||
- This module does not support installing or removing packages to/from an overlay as this is not supported
|
||||
by RHEL for Edge, packages needed should be defined in the osbuild Blueprint and provided to Image Builder
|
||||
at build time. This module exists only for C(package) module compatibility.
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Ensure htop and ansible are installed on rpm-ostree based RHEL
|
||||
ansible.posix.rhel_rpm_ostree:
|
||||
name:
|
||||
- htop
|
||||
- ansible
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = """
|
||||
msg:
|
||||
description: status of rpm transaction
|
||||
returned: always
|
||||
type: str
|
||||
sample: "No changes made."
|
||||
"""
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
def locally_installed(module, pkgname):
|
||||
(rc, out, err) = module.run_command('{0} -q {1}'.format(module.get_bin_path("rpm"), pkgname).split())
|
||||
return (rc == 0)
|
||||
|
||||
|
||||
def rpm_ostree_transaction(module):
|
||||
pkgs = []
|
||||
|
||||
if module.params['state'] in ['present', 'installed', 'latest']:
|
||||
for pkg in module.params['name']:
|
||||
if not locally_installed(module, pkg):
|
||||
pkgs.append(pkg)
|
||||
elif module.params['state'] in ['absent', 'removed']:
|
||||
for pkg in module.params['name']:
|
||||
if locally_installed(module, pkg):
|
||||
pkgs.append(pkg)
|
||||
|
||||
if not pkgs:
|
||||
module.exit_json(msg="No changes made.")
|
||||
else:
|
||||
if module.params['state'] in ['present', 'installed', 'latest']:
|
||||
module.fail_json(msg="The following packages are absent in the currently booted rpm-ostree commit: %s" ' '.join(pkgs))
|
||||
else:
|
||||
module.fail_json(msg="The following packages are present in the currently booted rpm-ostree commit: %s" ' '.join(pkgs))
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(type='list', elements='str', aliases=['pkg'], default=[]),
|
||||
state=dict(type='str', default=None, choices=['absent', 'installed', 'latest', 'present', 'removed']),
|
||||
),
|
||||
)
|
||||
|
||||
# Verify that the platform is an rpm-ostree based system
|
||||
if not os.path.exists("/run/ostree-booted"):
|
||||
module.fail_json(msg="Module rpm_ostree is only applicable for rpm-ostree based systems.")
|
||||
|
||||
try:
|
||||
rpm_ostree_transaction(module)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=to_text(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
125
plugins/modules/rpm_ostree_upgrade.py
Normal file
125
plugins/modules/rpm_ostree_upgrade.py
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: Red Hat Inc.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rpm_ostree_upgrade
|
||||
short_description: Manage rpm-ostree upgrade transactions
|
||||
description:
|
||||
- Manage an rpm-ostree upgrade transactions.
|
||||
version_added: 1.5.0
|
||||
author:
|
||||
- Adam Miller (@maxamillion)
|
||||
requirements:
|
||||
- rpm-ostree
|
||||
options:
|
||||
os:
|
||||
description:
|
||||
- The OSNAME upon which to operate.
|
||||
type: str
|
||||
default: ""
|
||||
required: false
|
||||
cache_only:
|
||||
description:
|
||||
- Perform the transaction using only pre-cached data, do not download.
|
||||
type: bool
|
||||
default: false
|
||||
required: false
|
||||
allow_downgrade:
|
||||
description:
|
||||
- Allow for the upgrade to be a chronologically older tree.
|
||||
type: bool
|
||||
default: false
|
||||
required: false
|
||||
peer:
|
||||
description:
|
||||
- Force peer-to-peer connection instead of using a system message bus.
|
||||
type: bool
|
||||
default: false
|
||||
required: false
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Upgrade the rpm-ostree image without options, accept all defaults
|
||||
ansible.posix.rpm_ostree_upgrade:
|
||||
|
||||
- name: Upgrade the rpm-ostree image allowing downgrades
|
||||
ansible.posix.rpm_ostree_upgrade:
|
||||
allow_downgrade: true
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
msg:
|
||||
description: The command standard output
|
||||
returned: always
|
||||
type: str
|
||||
sample: 'No upgrade available.'
|
||||
'''
|
||||
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_native, to_text
|
||||
|
||||
|
||||
def rpm_ostree_transaction(module):
|
||||
cmd = []
|
||||
cmd.append(module.get_bin_path("rpm-ostree"))
|
||||
cmd.append('upgrade')
|
||||
|
||||
if module.params['os']:
|
||||
cmd += ['--os', module.params['os']]
|
||||
if module.params['cache_only']:
|
||||
cmd += ['--cache-only']
|
||||
if module.params['allow_downgrade']:
|
||||
cmd += ['--allow-downgrade']
|
||||
if module.params['peer']:
|
||||
cmd += ['--peer']
|
||||
|
||||
module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C')
|
||||
|
||||
rc, out, err = module.run_command(cmd)
|
||||
|
||||
if rc != 0:
|
||||
module.fail_json(rc=rc, msg=err)
|
||||
else:
|
||||
if to_text("No upgrade available.") in to_text(out):
|
||||
module.exit_json(msg=out, changed=False)
|
||||
else:
|
||||
module.exit_json(msg=out, changed=True)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
os=dict(type='str', default=''),
|
||||
cache_only=dict(type='bool', default=False),
|
||||
allow_downgrade=dict(type='bool', default=False),
|
||||
peer=dict(type='bool', default=False),
|
||||
),
|
||||
)
|
||||
|
||||
# Verify that the platform is an rpm-ostree based system
|
||||
if not os.path.exists("/run/ostree-booted"):
|
||||
module.fail_json(msg="Module rpm_ostree_upgrade is only applicable for rpm-ostree based systems.")
|
||||
|
||||
try:
|
||||
rpm_ostree_transaction(module)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=to_native(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -7,25 +7,27 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: seboolean
|
||||
short_description: Toggles SELinux booleans
|
||||
description:
|
||||
- Toggles SELinux booleans.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the boolean to configure.
|
||||
required: true
|
||||
type: str
|
||||
persistent:
|
||||
description:
|
||||
- Set to C(yes) if the boolean setting should survive a reboot.
|
||||
- Set to V(true) if the boolean setting should survive a reboot.
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
state:
|
||||
description:
|
||||
- Desired boolean value
|
||||
- Desired boolean value.
|
||||
type: bool
|
||||
required: true
|
||||
ignore_selinux_state:
|
||||
@@ -38,6 +40,7 @@ notes:
|
||||
requirements:
|
||||
- libselinux-python
|
||||
- libsemanage-python
|
||||
- python3-libsemanage
|
||||
author:
|
||||
- Stephen Fromm (@sfromm)
|
||||
'''
|
||||
@@ -46,8 +49,8 @@ EXAMPLES = r'''
|
||||
- name: Set httpd_can_network_connect flag on and keep it persistent across reboots
|
||||
ansible.posix.seboolean:
|
||||
name: httpd_can_network_connect
|
||||
state: yes
|
||||
persistent: yes
|
||||
state: true
|
||||
persistent: true
|
||||
'''
|
||||
|
||||
import os
|
||||
@@ -70,31 +73,14 @@ except ImportError:
|
||||
HAVE_SEMANAGE = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.six import binary_type
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
||||
|
||||
|
||||
def get_runtime_status(ignore_selinux_state=False):
|
||||
return True if ignore_selinux_state is True else selinux.is_selinux_enabled()
|
||||
|
||||
|
||||
def has_boolean_value(module, name):
|
||||
bools = []
|
||||
try:
|
||||
rc, bools = selinux.security_get_boolean_names()
|
||||
except OSError:
|
||||
module.fail_json(msg="Failed to get list of boolean names")
|
||||
# work around for selinux who changed its API, see
|
||||
# https://github.com/ansible/ansible/issues/25651
|
||||
if len(bools) > 0:
|
||||
if isinstance(bools[0], binary_type):
|
||||
name = to_bytes(name)
|
||||
if name in bools:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_boolean_value(module, name):
|
||||
state = 0
|
||||
try:
|
||||
@@ -170,7 +156,10 @@ def semanage_set_boolean_value(module, handle, name, value):
|
||||
semanage.semanage_handle_destroy(handle)
|
||||
module.fail_json(msg="Failed to modify boolean key with semanage")
|
||||
|
||||
if semanage.semanage_bool_set_active(handle, boolkey, sebool) < 0:
|
||||
if (
|
||||
selinux.is_selinux_enabled()
|
||||
and semanage.semanage_bool_set_active(handle, boolkey, sebool) < 0
|
||||
):
|
||||
semanage.semanage_handle_destroy(handle)
|
||||
module.fail_json(msg="Failed to set boolean key active with semanage")
|
||||
|
||||
@@ -278,11 +267,17 @@ def main():
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not HAVE_SELINUX and not HAVE_SEMANAGE and HAS_RESPAWN_UTIL:
|
||||
# Only respawn the module if both libraries are missing.
|
||||
# If only one is available, then usage of the "wrong" (i.e. not the system one)
|
||||
# python interpreter is likely not the problem.
|
||||
respawn_module("selinux")
|
||||
|
||||
if not HAVE_SELINUX:
|
||||
module.fail_json(msg=missing_required_lib('libselinux-python'), exception=SELINUX_IMP_ERR)
|
||||
|
||||
if not HAVE_SEMANAGE:
|
||||
module.fail_json(msg=missing_required_lib('libsemanage-python'), exception=SEMANAGE_IMP_ERR)
|
||||
module.fail_json(msg=missing_required_lib('libsemanage-python or python3-libsemanage'), exception=SEMANAGE_IMP_ERR)
|
||||
|
||||
ignore_selinux_state = module.params['ignore_selinux_state']
|
||||
|
||||
@@ -305,12 +300,9 @@ def main():
|
||||
# Feature only available in selinux library since 2012.
|
||||
name = selinux.selinux_boolean_sub(name)
|
||||
|
||||
if not has_boolean_value(module, name):
|
||||
module.fail_json(msg="SELinux boolean %s does not exist." % name)
|
||||
|
||||
if persistent:
|
||||
changed = semanage_boolean_value(module, name, state)
|
||||
else:
|
||||
elif selinux.is_selinux_enabled():
|
||||
cur_value = get_boolean_value(module, name)
|
||||
if cur_value != state:
|
||||
changed = True
|
||||
|
||||
@@ -16,20 +16,31 @@ description:
|
||||
- Configures the SELinux mode and policy.
|
||||
- A reboot may be required after usage.
|
||||
- Ansible will not issue this reboot but will let you know when it is required.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
policy:
|
||||
description:
|
||||
- The name of the SELinux policy to use (e.g. C(targeted)) will be required if state is not C(disabled).
|
||||
- The name of the SELinux policy to use (e.g. C(targeted)) will be required unless O(state=disabled).
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- The SELinux mode.
|
||||
required: true
|
||||
choices: [ disabled, enforcing, permissive ]
|
||||
type: str
|
||||
update_kernel_param:
|
||||
description:
|
||||
- If set to V(true), will update also the kernel boot parameters when disabling/enabling SELinux.
|
||||
- The C(grubby) tool must be present on the target system for this to work.
|
||||
default: false
|
||||
type: bool
|
||||
version_added: '1.4.0'
|
||||
configfile:
|
||||
description:
|
||||
- The path to the SELinux configuration file, if non-standard.
|
||||
default: /etc/selinux/config
|
||||
aliases: [ conf, file ]
|
||||
type: str
|
||||
requirements: [ libselinux-python ]
|
||||
author:
|
||||
- Derek Carter (@goozbach) <goozbach@friocorte.com>
|
||||
@@ -93,8 +104,11 @@ except ImportError:
|
||||
HAS_SELINUX = False
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||
from ansible.module_utils.common.process import get_bin_path
|
||||
from ansible.module_utils.facts.utils import get_file_lines
|
||||
|
||||
from ansible_collections.ansible.posix.plugins.module_utils._respawn import respawn_module, HAS_RESPAWN_UTIL
|
||||
|
||||
|
||||
# getter subroutines
|
||||
def get_config_state(configfile):
|
||||
@@ -115,6 +129,34 @@ def get_config_policy(configfile):
|
||||
return line.split('=')[1].strip()
|
||||
|
||||
|
||||
def get_kernel_enabled(module, grubby_bin):
|
||||
if grubby_bin is None:
|
||||
module.fail_json(msg="'grubby' command not found on host",
|
||||
details="In order to update the kernel command line"
|
||||
"enabled/disabled setting, the grubby package"
|
||||
"needs to be present on the system.")
|
||||
|
||||
rc, stdout, stderr = module.run_command([grubby_bin, '--info=ALL'])
|
||||
if rc != 0:
|
||||
module.fail_json(msg="unable to run grubby")
|
||||
|
||||
all_enabled = True
|
||||
all_disabled = True
|
||||
for line in stdout.split('\n'):
|
||||
match = re.match('^args="(.*)"$', line)
|
||||
if match is None:
|
||||
continue
|
||||
args = match.group(1).split(' ')
|
||||
if 'selinux=0' in args:
|
||||
all_enabled = False
|
||||
else:
|
||||
all_disabled = False
|
||||
if all_disabled == all_enabled:
|
||||
# inconsistent config - return None to force update
|
||||
return None
|
||||
return all_enabled
|
||||
|
||||
|
||||
# setter subroutines
|
||||
def set_config_state(module, state, configfile):
|
||||
# SELINUX=permissive
|
||||
@@ -125,9 +167,15 @@ def set_config_state(module, state, configfile):
|
||||
tmpfd, tmpfile = tempfile.mkstemp()
|
||||
|
||||
with open(tmpfile, "w") as write_file:
|
||||
line_found = False
|
||||
for line in lines:
|
||||
if re.match(r'^SELINUX=.*$', line):
|
||||
line_found = True
|
||||
write_file.write(re.sub(r'^SELINUX=.*', stateline, line) + '\n')
|
||||
|
||||
if not line_found:
|
||||
write_file.write('SELINUX=%s\n' % state)
|
||||
|
||||
module.atomic_move(tmpfile, configfile)
|
||||
|
||||
|
||||
@@ -143,6 +191,17 @@ def set_state(module, state):
|
||||
module.fail_json(msg=msg)
|
||||
|
||||
|
||||
def set_kernel_enabled(module, grubby_bin, value):
|
||||
rc, stdout, stderr = module.run_command([grubby_bin, '--update-kernel=ALL',
|
||||
'--remove-args' if value else '--args',
|
||||
'selinux=0'])
|
||||
if rc != 0:
|
||||
if value:
|
||||
module.fail_json(msg='unable to remove selinux=0 from kernel config')
|
||||
else:
|
||||
module.fail_json(msg='unable to add selinux=0 to kernel config')
|
||||
|
||||
|
||||
def set_config_policy(module, policy, configfile):
|
||||
if not os.path.exists('/etc/selinux/%s/policy' % policy):
|
||||
module.fail_json(msg='Policy %s does not exist in /etc/selinux/' % policy)
|
||||
@@ -155,9 +214,15 @@ def set_config_policy(module, policy, configfile):
|
||||
tmpfd, tmpfile = tempfile.mkstemp()
|
||||
|
||||
with open(tmpfile, "w") as write_file:
|
||||
line_found = False
|
||||
for line in lines:
|
||||
if re.match(r'^SELINUXTYPE=.*$', line):
|
||||
line_found = True
|
||||
write_file.write(re.sub(r'^SELINUXTYPE=.*', policyline, line) + '\n')
|
||||
|
||||
if not line_found:
|
||||
write_file.write('SELINUXTYPE=%s\n' % policy)
|
||||
|
||||
module.atomic_move(tmpfile, configfile)
|
||||
|
||||
|
||||
@@ -165,13 +230,16 @@ def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
policy=dict(type='str'),
|
||||
state=dict(type='str', required='True', choices=['enforcing', 'permissive', 'disabled']),
|
||||
state=dict(type='str', required=True, choices=['enforcing', 'permissive', 'disabled']),
|
||||
configfile=dict(type='str', default='/etc/selinux/config', aliases=['conf', 'file']),
|
||||
update_kernel_param=dict(type='bool', default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if not HAS_SELINUX:
|
||||
if HAS_RESPAWN_UTIL:
|
||||
respawn_module("selinux")
|
||||
module.fail_json(msg=missing_required_lib('libselinux-python'), exception=SELINUX_IMP_ERR)
|
||||
|
||||
# global vars
|
||||
@@ -180,9 +248,11 @@ def main():
|
||||
configfile = module.params['configfile']
|
||||
policy = module.params['policy']
|
||||
state = module.params['state']
|
||||
update_kernel_param = module.params['update_kernel_param']
|
||||
runtime_enabled = selinux.is_selinux_enabled()
|
||||
runtime_policy = selinux.selinux_getpolicytype()[1]
|
||||
runtime_state = 'disabled'
|
||||
kernel_enabled = None
|
||||
reboot_required = False
|
||||
|
||||
if runtime_enabled:
|
||||
@@ -199,6 +269,12 @@ def main():
|
||||
|
||||
config_policy = get_config_policy(configfile)
|
||||
config_state = get_config_state(configfile)
|
||||
if update_kernel_param:
|
||||
try:
|
||||
grubby_bin = get_bin_path('grubby')
|
||||
except ValueError:
|
||||
grubby_bin = None
|
||||
kernel_enabled = get_kernel_enabled(module, grubby_bin)
|
||||
|
||||
# check to see if policy is set if state is not 'disabled'
|
||||
if state != 'disabled':
|
||||
@@ -253,6 +329,21 @@ def main():
|
||||
msgs.append("Config SELinux state changed from '%s' to '%s'" % (config_state, state))
|
||||
changed = True
|
||||
|
||||
requested_kernel_enabled = state in ('enforcing', 'permissive')
|
||||
# Update kernel enabled/disabled config only when setting is consistent
|
||||
# across all kernels AND the requested state differs from the current state
|
||||
if update_kernel_param and kernel_enabled != requested_kernel_enabled:
|
||||
if not module.check_mode:
|
||||
set_kernel_enabled(module, grubby_bin, requested_kernel_enabled)
|
||||
if requested_kernel_enabled:
|
||||
states = ('disabled', 'enabled')
|
||||
else:
|
||||
states = ('enabled', 'disabled')
|
||||
if kernel_enabled is None:
|
||||
states = ('<inconsistent>', states[1])
|
||||
msgs.append("Kernel SELinux state changed from '%s' to '%s'" % states)
|
||||
changed = True
|
||||
|
||||
module.exit_json(changed=changed, msg=', '.join(msgs), configfile=configfile, policy=policy, state=state, reboot_required=reboot_required)
|
||||
|
||||
|
||||
|
||||
@@ -14,75 +14,76 @@ DOCUMENTATION = r'''
|
||||
module: synchronize
|
||||
short_description: A wrapper around rsync to make common tasks in your playbooks quick and easy
|
||||
description:
|
||||
- C(synchronize) is a wrapper around rsync to make common tasks in your playbooks quick and easy.
|
||||
- M(ansible.posix.synchronize) is a wrapper around C(rsync) to make common tasks in your playbooks quick and easy.
|
||||
- It is run and originates on the local host where Ansible is being run.
|
||||
- Of course, you could just use the C(command) action to call rsync yourself, but you also have to add a fair number of
|
||||
- Of course, you could just use the M(ansible.builtin.command) action to call C(rsync) yourself, but you also have to add a fair number of
|
||||
boilerplate options and host facts.
|
||||
- This module is not intended to provide access to the full power of rsync, but does make the most common
|
||||
invocations easier to implement. You `still` may need to call rsync directly via C(command) or C(shell) depending on your use case.
|
||||
- This module is not intended to provide access to the full power of C(rsync), but does make the most common
|
||||
invocations easier to implement.
|
||||
You I(still) may need to call C(rsync) directly via M(ansible.builtin.command) or M(ansible.builtin.shell) depending on your use case.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
src:
|
||||
description:
|
||||
- Path on the source host that will be synchronized to the destination.
|
||||
- The path can be absolute or relative.
|
||||
type: str
|
||||
type: path
|
||||
required: true
|
||||
dest:
|
||||
description:
|
||||
- Path on the destination host that will be synchronized from the source.
|
||||
- The path can be absolute or relative.
|
||||
type: str
|
||||
type: path
|
||||
required: true
|
||||
dest_port:
|
||||
description:
|
||||
- Port number for ssh on the destination host.
|
||||
- Prior to Ansible 2.0, the ansible_ssh_port inventory var took precedence over this value.
|
||||
- This parameter defaults to the value of C(ansible_ssh_port) or C(ansible_port),
|
||||
the C(remote_port) config setting or the value from ssh client configuration
|
||||
if none of the former have been set.
|
||||
- Prior to Ansible 2.0, the C(ansible_ssh_port) inventory var took precedence over this value.
|
||||
- This parameter defaults to the value of C(ansible_port), the C(remote_port) config setting
|
||||
or the value from ssh client configuration if none of the former have been set.
|
||||
type: int
|
||||
mode:
|
||||
description:
|
||||
- Specify the direction of the synchronization.
|
||||
- In push mode the localhost or delegate is the source.
|
||||
- In pull mode the remote host in context is the source.
|
||||
- In V(push) mode the localhost or delegate is the source.
|
||||
- In V(pull) mode the remote host in context is the source.
|
||||
type: str
|
||||
choices: [ pull, push ]
|
||||
default: push
|
||||
archive:
|
||||
description:
|
||||
- Mirrors the rsync archive flag, enables recursive, links, perms, times, owner, group flags and -D.
|
||||
- Mirrors the rsync archive flag, enables recursive, links, perms, times, owner, group flags, and C(-D).
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
checksum:
|
||||
description:
|
||||
- Skip based on checksum, rather than mod-time & size; Note that that "archive" option is still enabled by default - the "checksum" option will
|
||||
not disable it.
|
||||
- Skip based on checksum, rather than mod-time & size; Note that that O(archive) option is still enabled by default -
|
||||
the O(checksum) option will not disable it.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
compress:
|
||||
description:
|
||||
- Compress file data during the transfer.
|
||||
- In most cases, leave this enabled unless it causes problems.
|
||||
type: bool
|
||||
default: yes
|
||||
default: true
|
||||
existing_only:
|
||||
description:
|
||||
- Skip creating new files on receiver.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
delete:
|
||||
description:
|
||||
- Delete files in C(dest) that don't exist (after transfer, not before) in the C(src) path.
|
||||
- This option requires C(recursive=yes).
|
||||
- This option ignores excluded files and behaves like the rsync opt --delete-excluded.
|
||||
- Delete files in O(dest) that do not exist (after transfer, not before) in the O(src) path.
|
||||
- This option requires O(recursive=true).
|
||||
- This option ignores excluded files and behaves like the rsync opt C(--delete-after).
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
dirs:
|
||||
description:
|
||||
- Transfer directories without recursing.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
recursive:
|
||||
description:
|
||||
- Recurse into directories.
|
||||
@@ -97,7 +98,7 @@ options:
|
||||
description:
|
||||
- Copy symlinks as the item that they point to (the referent) is copied, rather than the symlink.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
perms:
|
||||
description:
|
||||
- Preserve permissions.
|
||||
@@ -130,32 +131,38 @@ options:
|
||||
default: 0
|
||||
set_remote_user:
|
||||
description:
|
||||
- Put user@ for the remote paths.
|
||||
- Put C(user@) for the remote paths.
|
||||
- If you have a custom ssh config to define the remote user for a host
|
||||
that does not match the inventory user, you should set this parameter to C(no).
|
||||
that does not match the inventory user, you should set this parameter to V(false).
|
||||
type: bool
|
||||
default: yes
|
||||
use_ssh_args:
|
||||
default: true
|
||||
ssh_connection_multiplexing:
|
||||
description:
|
||||
- Use the ssh_args specified in ansible.cfg.
|
||||
- SSH connection multiplexing for rsync is disabled by default to prevent misconfigured ControlSockets from resulting in failed SSH connections.
|
||||
This is accomplished by setting the SSH C(ControlSocket) to C(none).
|
||||
- Set this option to V(true) to allow multiplexing and reduce SSH connection overhead.
|
||||
- Note that simply setting this option to V(true) is not enough;
|
||||
You must also configure SSH connection multiplexing in your SSH client config by setting values for
|
||||
C(ControlMaster), C(ControlPersist) and C(ControlPath).
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
rsync_opts:
|
||||
description:
|
||||
- Specify additional rsync options by passing in an array.
|
||||
- Note that an empty string in C(rsync_opts) will end up transfer the current working directory.
|
||||
type: list
|
||||
default:
|
||||
default: []
|
||||
elements: str
|
||||
partial:
|
||||
description:
|
||||
- Tells rsync to keep the partial file which should make a subsequent transfer of the rest of the file much faster.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
verify_host:
|
||||
description:
|
||||
- Verify destination host key.
|
||||
type: bool
|
||||
default: no
|
||||
default: false
|
||||
private_key:
|
||||
description:
|
||||
- Specify the private key to use for SSH-based rsync connections (e.g. C(~/.ssh/id_rsa)).
|
||||
@@ -165,32 +172,65 @@ options:
|
||||
- Add a destination to hard link against during the rsync.
|
||||
type: list
|
||||
default:
|
||||
elements: path
|
||||
delay_updates:
|
||||
description:
|
||||
- This option puts the temporary file from each updated file into a holding directory until the end of the transfer,
|
||||
at which time all the files are renamed into place in rapid succession.
|
||||
type: bool
|
||||
default: true
|
||||
version_added: '1.3.0'
|
||||
use_ssh_args:
|
||||
description:
|
||||
- In Ansible 2.10 and lower, it uses the ssh_args specified in C(ansible.cfg).
|
||||
- In Ansible 2.11 and onwards, when set to V(true), it uses all SSH connection configurations like
|
||||
C(ansible_ssh_args), C(ansible_ssh_common_args), and C(ansible_ssh_extra_args).
|
||||
type: bool
|
||||
default: false
|
||||
_local_rsync_path:
|
||||
description: Internal use only.
|
||||
type: path
|
||||
default: 'rsync'
|
||||
required: false
|
||||
_local_rsync_password:
|
||||
description: Internal use only, never logged.
|
||||
type: str
|
||||
required: false
|
||||
_substitute_controller:
|
||||
description: Internal use only.
|
||||
type: bool
|
||||
default: false
|
||||
_ssh_args:
|
||||
description: Internal use only. See O(use_ssh_args) for ssh arg settings.
|
||||
type: str
|
||||
required: false
|
||||
|
||||
notes:
|
||||
- rsync must be installed on both the local and remote host.
|
||||
- For the C(synchronize) module, the "local host" is the host `the synchronize task originates on`, and the "destination host" is the host
|
||||
`synchronize is connecting to`.
|
||||
- The "local host" can be changed to a different host by using `delegate_to`. This enables copying between two remote hosts or entirely on one
|
||||
remote machine.
|
||||
- C(rsync) must be installed on both the local and remote host.
|
||||
- For the M(ansible.posix.synchronize) module, the "local host" is the host I(the synchronize task originates on),
|
||||
and the "destination host" is the host I(synchronize is connecting to).
|
||||
- The "local host" can be changed to a different host by using C(delegate_to).
|
||||
This enables copying between two remote hosts or entirely on one remote machine.
|
||||
- >
|
||||
The user and permissions for the synchronize `src` are those of the user running the Ansible task on the local host (or the remote_user for a
|
||||
delegate_to host when delegate_to is used).
|
||||
The user and permissions for the synchronize O(src) are those of the user running the Ansible task on the local host (or the remote_user for a
|
||||
C(delegate_to) host when C(delegate_to) is used).
|
||||
- The user and permissions for the synchronize `dest` are those of the `remote_user` on the destination host or the `become_user` if `become=yes` is active.
|
||||
- In Ansible 2.0 a bug in the synchronize module made become occur on the "local host". This was fixed in Ansible 2.0.1.
|
||||
- Currently, synchronize is limited to elevating permissions via passwordless sudo. This is because rsync itself is connecting to the remote machine
|
||||
and rsync doesn't give us a way to pass sudo credentials in.
|
||||
- Currently, M(ansible.posix.synchronize) is limited to elevating permissions via passwordless sudo.
|
||||
This is because rsync itself is connecting to the remote machine and rsync doesn't give us a way to pass sudo credentials in.
|
||||
- Currently there are only a few connection types which support synchronize (ssh, paramiko, local, and docker) because a sync strategy has been
|
||||
determined for those connection types. Note that the connection for these must not need a password as rsync itself is making the connection and
|
||||
rsync does not provide us a way to pass a password to the connection.
|
||||
- Expect that dest=~/x will be ~<remote_user>/x even if using sudo.
|
||||
- Expect that O(dest=~/x) will be V(~<remote_user>/x) even if using sudo.
|
||||
- Inspect the verbose output to validate the destination user/host/path are what was expected.
|
||||
- To exclude files and directories from being synchronized, you may add C(.rsync-filter) files to the source directory.
|
||||
- rsync daemon must be up and running with correct permission when using rsync protocol in source or destination path.
|
||||
- The C(synchronize) module forces `--delay-updates` to avoid leaving a destination in a broken in-between state if the underlying rsync process
|
||||
encounters an error. Those synchronizing large numbers of files that are willing to trade safety for performance should call rsync directly.
|
||||
- The C(synchronize) module enables C(--delay-updates) by default to avoid leaving a destination in a broken in-between state if the underlying rsync process
|
||||
encounters an error. Those synchronizing large numbers of files that are willing to trade safety for performance should disable this option.
|
||||
- link_destination is subject to the same limitations as the underlying rsync daemon. Hard links are only preserved if the relative subtrees
|
||||
of the source and destination are the same. Attempts to hardlink into a directory that is a subdirectory of the source will be prevented.
|
||||
seealso:
|
||||
- module: copy
|
||||
- module: ansible.builtin.copy
|
||||
- module: community.windows.win_robocopy
|
||||
author:
|
||||
- Timothy Appnel (@tima)
|
||||
@@ -213,7 +253,7 @@ EXAMPLES = r'''
|
||||
src: rsync://somehost.com/path/
|
||||
dest: /some/absolute/path/
|
||||
|
||||
- name: Synchronization using rsync protocol on delegate host (push)
|
||||
- name: Synchronization using rsync protocol on delegate host (push)
|
||||
ansible.posix.synchronize:
|
||||
src: /some/absolute/path/
|
||||
dest: rsync://somehost.com/path/
|
||||
@@ -230,27 +270,27 @@ EXAMPLES = r'''
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
archive: no
|
||||
archive: false
|
||||
|
||||
- name: Synchronization with --archive options enabled except for --recursive
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
recursive: no
|
||||
recursive: false
|
||||
|
||||
- name: Synchronization with --archive options enabled except for --times, with --checksum option enabled
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
checksum: yes
|
||||
times: no
|
||||
checksum: true
|
||||
times: false
|
||||
|
||||
- name: Synchronization without --archive options enabled except use --links
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
archive: no
|
||||
links: yes
|
||||
archive: false
|
||||
links: true
|
||||
|
||||
- name: Synchronization of two paths both on the control machine
|
||||
ansible.posix.synchronize:
|
||||
@@ -280,8 +320,8 @@ EXAMPLES = r'''
|
||||
ansible.posix.synchronize:
|
||||
src: some/relative/path
|
||||
dest: /some/absolute/path
|
||||
delete: yes
|
||||
recursive: yes
|
||||
delete: true
|
||||
recursive: true
|
||||
|
||||
# This specific command is granted su privileges on the destination
|
||||
- name: Synchronize using an alternate rsync command
|
||||
@@ -313,7 +353,7 @@ EXAMPLES = r'''
|
||||
# Specify the rsync binary to use on remote host and on local host
|
||||
- hosts: groupofhosts
|
||||
vars:
|
||||
ansible_rsync_path: /usr/gnu/bin/rsync
|
||||
ansible_rsync_path: /usr/gnu/bin/rsync
|
||||
|
||||
tasks:
|
||||
- name: copy /tmp/localpath/ to remote location /tmp/remotepath
|
||||
@@ -340,11 +380,11 @@ def substitute_controller(path):
|
||||
if not client_addr:
|
||||
ssh_env_string = os.environ.get('SSH_CLIENT', None)
|
||||
try:
|
||||
client_addr, _ = ssh_env_string.split(None, 1)
|
||||
client_addr, _ = ssh_env_string.split(None, 1) # pylint: disable=disallowed-name
|
||||
except AttributeError:
|
||||
ssh_env_string = os.environ.get('SSH_CONNECTION', None)
|
||||
try:
|
||||
client_addr, _ = ssh_env_string.split(None, 1)
|
||||
client_addr, _ = ssh_env_string.split(None, 1) # pylint: disable=disallowed-name
|
||||
except AttributeError:
|
||||
pass
|
||||
if not client_addr:
|
||||
@@ -366,8 +406,8 @@ def is_rsh_needed(source, dest):
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
src=dict(type='str', required=True),
|
||||
dest=dict(type='str', required=True),
|
||||
src=dict(type='path', required=True),
|
||||
dest=dict(type='path', required=True),
|
||||
dest_port=dict(type='int'),
|
||||
delete=dict(type='bool', default=False),
|
||||
private_key=dict(type='path'),
|
||||
@@ -389,12 +429,15 @@ def main():
|
||||
group=dict(type='bool'),
|
||||
set_remote_user=dict(type='bool', default=True),
|
||||
rsync_timeout=dict(type='int', default=0),
|
||||
rsync_opts=dict(type='list', default=[]),
|
||||
ssh_args=dict(type='str'),
|
||||
rsync_opts=dict(type='list', default=[], elements='str'),
|
||||
_ssh_args=dict(type='str'),
|
||||
use_ssh_args=dict(type='bool', default=False),
|
||||
ssh_connection_multiplexing=dict(type='bool', default=False),
|
||||
partial=dict(type='bool', default=False),
|
||||
verify_host=dict(type='bool', default=False),
|
||||
delay_updates=dict(type='bool', default=True),
|
||||
mode=dict(type='str', default='push', choices=['pull', 'push']),
|
||||
link_dest=dict(type='list')
|
||||
link_dest=dict(type='list', elements='path'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
@@ -430,14 +473,16 @@ def main():
|
||||
owner = module.params['owner']
|
||||
group = module.params['group']
|
||||
rsync_opts = module.params['rsync_opts']
|
||||
ssh_args = module.params['ssh_args']
|
||||
ssh_args = module.params['_ssh_args']
|
||||
ssh_connection_multiplexing = module.params['ssh_connection_multiplexing']
|
||||
verify_host = module.params['verify_host']
|
||||
link_dest = module.params['link_dest']
|
||||
delay_updates = module.params['delay_updates']
|
||||
|
||||
if '/' not in rsync:
|
||||
rsync = module.get_bin_path(rsync, required=True)
|
||||
|
||||
cmd = [rsync, '--delay-updates', '-F']
|
||||
cmd = [rsync]
|
||||
_sshpass_pipe = None
|
||||
if rsync_password:
|
||||
try:
|
||||
@@ -448,6 +493,9 @@ def main():
|
||||
)
|
||||
_sshpass_pipe = os.pipe()
|
||||
cmd = ['sshpass', '-d' + to_native(_sshpass_pipe[0], errors='surrogate_or_strict')] + cmd
|
||||
if delay_updates:
|
||||
cmd.append('--delay-updates')
|
||||
cmd.append('-F')
|
||||
if compress:
|
||||
cmd.append('--compress')
|
||||
if rsync_timeout:
|
||||
@@ -506,7 +554,9 @@ def main():
|
||||
|
||||
# if the user has not supplied an --rsh option go ahead and add ours
|
||||
if not has_rsh:
|
||||
ssh_cmd = [module.get_bin_path('ssh', required=True), '-S', 'none']
|
||||
ssh_cmd = [module.get_bin_path('ssh', required=True)]
|
||||
if not ssh_connection_multiplexing:
|
||||
ssh_cmd.extend(['-S', 'none'])
|
||||
if private_key is not None:
|
||||
ssh_cmd.extend(['-i', private_key])
|
||||
# If the user specified a port value
|
||||
@@ -519,10 +569,10 @@ def main():
|
||||
ssh_cmd_str = ' '.join(shlex_quote(arg) for arg in ssh_cmd)
|
||||
if ssh_args:
|
||||
ssh_cmd_str += ' %s' % ssh_args
|
||||
cmd.append('--rsh=%s' % ssh_cmd_str)
|
||||
cmd.append('--rsh=%s' % shlex_quote(ssh_cmd_str))
|
||||
|
||||
if rsync_path:
|
||||
cmd.append('--rsync-path=%s' % rsync_path)
|
||||
cmd.append('--rsync-path=%s' % shlex_quote(rsync_path))
|
||||
|
||||
if rsync_opts:
|
||||
if '' in rsync_opts:
|
||||
@@ -541,23 +591,17 @@ def main():
|
||||
# hardlink is actually a change
|
||||
cmd.append('-vv')
|
||||
for x in link_dest:
|
||||
link_path = os.path.abspath(os.path.expanduser(x))
|
||||
link_path = os.path.abspath(x)
|
||||
destination_path = os.path.abspath(os.path.dirname(dest))
|
||||
if destination_path.find(link_path) == 0:
|
||||
module.fail_json(msg='Hardlinking into a subdirectory of the source would cause recursion. %s and %s' % (destination_path, dest))
|
||||
cmd.append('--link-dest=%s' % link_path)
|
||||
|
||||
changed_marker = '<<CHANGED>>'
|
||||
cmd.append('--out-format=' + changed_marker + '%i %n%L')
|
||||
cmd.append('--out-format=%s' % shlex_quote(changed_marker + '%i %n%L'))
|
||||
|
||||
# expand the paths
|
||||
if '@' not in source:
|
||||
source = os.path.expanduser(source)
|
||||
if '@' not in dest:
|
||||
dest = os.path.expanduser(dest)
|
||||
|
||||
cmd.append(source)
|
||||
cmd.append(dest)
|
||||
cmd.append(shlex_quote(source))
|
||||
cmd.append(shlex_quote(dest))
|
||||
cmdstr = ' '.join(cmd)
|
||||
|
||||
# If we are using password authentication, write the password into the pipe
|
||||
@@ -572,10 +616,10 @@ def main():
|
||||
raise
|
||||
|
||||
(rc, out, err) = module.run_command(
|
||||
cmd, pass_fds=_sshpass_pipe,
|
||||
cmdstr, pass_fds=_sshpass_pipe,
|
||||
before_communicate_callback=_write_password_to_pipe)
|
||||
else:
|
||||
(rc, out, err) = module.run_command(cmd)
|
||||
(rc, out, err) = module.run_command(cmdstr)
|
||||
|
||||
if rc:
|
||||
return module.fail_json(msg=err, rc=rc, cmd=cmdstr)
|
||||
|
||||
@@ -9,49 +9,55 @@ from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: sysctl
|
||||
short_description: Manage entries in sysctl.conf.
|
||||
description:
|
||||
- This module manipulates sysctl entries and optionally performs a C(/sbin/sysctl -p) after changing them.
|
||||
version_added: "1.0.0"
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The dot-separated path (aka I(key)) specifying the sysctl variable.
|
||||
- The dot-separated path (also known as O(key)) specifying the sysctl variable.
|
||||
required: true
|
||||
aliases: [ 'key' ]
|
||||
type: str
|
||||
value:
|
||||
description:
|
||||
- Desired value of the sysctl key.
|
||||
aliases: [ 'val' ]
|
||||
type: str
|
||||
state:
|
||||
description:
|
||||
- Whether the entry should be present or absent in the sysctl file.
|
||||
choices: [ "present", "absent" ]
|
||||
default: present
|
||||
type: str
|
||||
ignoreerrors:
|
||||
description:
|
||||
- Use this option to ignore errors about unknown keys.
|
||||
type: bool
|
||||
default: 'no'
|
||||
default: false
|
||||
reload:
|
||||
description:
|
||||
- If C(yes), performs a I(/sbin/sysctl -p) if the C(sysctl_file) is
|
||||
updated. If C(no), does not reload I(sysctl) even if the
|
||||
C(sysctl_file) is updated.
|
||||
- If V(true), performs a C(/sbin/sysctl -p) if the O(sysctl_file) is
|
||||
updated. If V(false), does not reload C(sysctl) even if the
|
||||
O(sysctl_file) is updated.
|
||||
type: bool
|
||||
default: 'yes'
|
||||
default: true
|
||||
sysctl_file:
|
||||
description:
|
||||
- Specifies the absolute path to C(sysctl.conf), if not C(/etc/sysctl.conf).
|
||||
default: /etc/sysctl.conf
|
||||
type: path
|
||||
sysctl_set:
|
||||
description:
|
||||
- Verify token value with the sysctl command and set with -w if necessary
|
||||
- Verify token value with the sysctl command and set with C(-w) if necessary.
|
||||
type: bool
|
||||
default: 'no'
|
||||
author: "David CHANIAL (@davixx) <david.chanial@gmail.com>"
|
||||
default: false
|
||||
author:
|
||||
- David CHANIAL (@davixx)
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
@@ -72,21 +78,28 @@ EXAMPLES = r'''
|
||||
name: kernel.panic
|
||||
value: '3'
|
||||
sysctl_file: /tmp/test_sysctl.conf
|
||||
reload: no
|
||||
reload: false
|
||||
|
||||
# Enable resource limits management in FreeBSD
|
||||
- ansible.posix.sysctl:
|
||||
name: kern.racct.enable
|
||||
value: '1'
|
||||
sysctl_file: /boot/loader.conf
|
||||
reload: false
|
||||
|
||||
# Set ip forwarding on in /proc and verify token value with the sysctl command
|
||||
- ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
sysctl_set: yes
|
||||
sysctl_set: true
|
||||
|
||||
# Set ip forwarding on in /proc and in the sysctl file and reload if necessary
|
||||
- ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
sysctl_set: yes
|
||||
sysctl_set: true
|
||||
state: present
|
||||
reload: yes
|
||||
reload: true
|
||||
'''
|
||||
|
||||
# ==============================================================
|
||||
@@ -95,6 +108,7 @@ import os
|
||||
import platform
|
||||
import re
|
||||
import tempfile
|
||||
import glob
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import string_types
|
||||
@@ -108,12 +122,24 @@ class SysctlModule(object):
|
||||
# success or failure.
|
||||
LANG_ENV = {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}
|
||||
|
||||
# We define a variable to keep all the directories to be read, equivalent to
|
||||
# (/sbin/sysctl --system) option
|
||||
SYSCTL_DIRS = [
|
||||
'/etc/sysctl.d/*.conf',
|
||||
'/run/sysctl.d/*.conf',
|
||||
'/usr/local/lib/sysctl.d/*.conf',
|
||||
'/usr/lib/sysctl.d/*.conf',
|
||||
'/lib/sysctl.d/*.conf',
|
||||
'/etc/sysctl.conf'
|
||||
]
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.args = self.module.params
|
||||
|
||||
self.sysctl_cmd = self.module.get_bin_path('sysctl', required=True)
|
||||
self.sysctl_file = self.args['sysctl_file']
|
||||
self.system_Wide = self.args['system_Wide']
|
||||
|
||||
self.proc_value = None # current token value in proc fs
|
||||
self.file_value = None # current token value in file
|
||||
@@ -181,12 +207,12 @@ class SysctlModule(object):
|
||||
|
||||
# Do the work
|
||||
if not self.module.check_mode:
|
||||
if self.set_proc:
|
||||
self.set_token_value(self.args['name'], self.args['value'])
|
||||
if self.write_file:
|
||||
self.write_sysctl()
|
||||
if self.changed and self.args['reload']:
|
||||
self.reload_sysctl()
|
||||
if self.set_proc:
|
||||
self.set_token_value(self.args['name'], self.args['value'])
|
||||
|
||||
def _values_is_equal(self, a, b):
|
||||
"""Expects two string values. It will split the string by whitespace
|
||||
@@ -293,15 +319,22 @@ class SysctlModule(object):
|
||||
# https://github.com/ansible/ansible/issues/58158
|
||||
return
|
||||
else:
|
||||
# system supports reloading via the -p flag to sysctl, so we'll use that
|
||||
sysctl_args = [self.sysctl_cmd, '-p', self.sysctl_file]
|
||||
if self.args['ignoreerrors']:
|
||||
sysctl_args.insert(1, '-e')
|
||||
if self.system_Wide:
|
||||
for sysctl_file in self.SYSCTL_DIRS:
|
||||
for conf_file in glob.glob(sysctl_file):
|
||||
rc, out, err = self.module.run_command([self.sysctl_cmd, '-p', conf_file], environ_update=self.LANG_ENV)
|
||||
if rc != 0 or self._stderr_failed(err):
|
||||
self.module.fail_json(msg="Failed to reload sysctl: %s" % to_native(out) + to_native(err))
|
||||
else:
|
||||
# system supports reloading via the -p flag to sysctl, so we'll use that
|
||||
sysctl_args = [self.sysctl_cmd, '-p', self.sysctl_file]
|
||||
if self.args['ignoreerrors']:
|
||||
sysctl_args.insert(1, '-e')
|
||||
|
||||
rc, out, err = self.module.run_command(sysctl_args, environ_update=self.LANG_ENV)
|
||||
rc, out, err = self.module.run_command(sysctl_args, environ_update=self.LANG_ENV)
|
||||
|
||||
if rc != 0 or self._stderr_failed(err):
|
||||
self.module.fail_json(msg="Failed to reload sysctl: %s" % to_native(out) + to_native(err))
|
||||
if rc != 0 or self._stderr_failed(err):
|
||||
self.module.fail_json(msg="Failed to reload sysctl: %s" % to_native(out) + to_native(err))
|
||||
|
||||
# ==============================================================
|
||||
# SYSCTL FILE MANAGEMENT
|
||||
@@ -360,7 +393,7 @@ class SysctlModule(object):
|
||||
# Completely rewrite the sysctl file
|
||||
def write_sysctl(self):
|
||||
# open a tmp file
|
||||
fd, tmp_path = tempfile.mkstemp('.conf', '.ansible_m_sysctl_', os.path.dirname(self.sysctl_file))
|
||||
fd, tmp_path = tempfile.mkstemp('.conf', '.ansible_m_sysctl_', os.path.dirname(os.path.realpath(self.sysctl_file)))
|
||||
f = open(tmp_path, "w")
|
||||
try:
|
||||
for l in self.fixed_lines:
|
||||
@@ -371,7 +404,7 @@ class SysctlModule(object):
|
||||
f.close()
|
||||
|
||||
# replace the real one
|
||||
self.module.atomic_move(tmp_path, self.sysctl_file)
|
||||
self.module.atomic_move(tmp_path, os.path.realpath(self.sysctl_file))
|
||||
|
||||
|
||||
# ==============================================================
|
||||
@@ -388,7 +421,8 @@ def main():
|
||||
reload=dict(default=True, type='bool'),
|
||||
sysctl_set=dict(default=False, type='bool'),
|
||||
ignoreerrors=dict(default=False, type='bool'),
|
||||
sysctl_file=dict(default='/etc/sysctl.conf', type='path')
|
||||
sysctl_file=dict(default='/etc/sysctl.conf', type='path'),
|
||||
system_wide=dict(default=False, type='bool'), # system_wide parameter
|
||||
),
|
||||
supports_check_mode=True,
|
||||
required_if=[('state', 'present', ['value'])],
|
||||
|
||||
@@ -4,13 +4,8 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.six import text_type
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.plugins.shell import ShellBase
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: csh
|
||||
plugin_type: shell
|
||||
short_description: C shell (/bin/csh)
|
||||
description:
|
||||
- When you have no other option than to use csh
|
||||
@@ -18,6 +13,10 @@ DOCUMENTATION = '''
|
||||
- shell_common
|
||||
'''
|
||||
|
||||
from ansible.module_utils.six import text_type
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.plugins.shell import ShellBase
|
||||
|
||||
|
||||
class ShellModule(ShellBase):
|
||||
|
||||
@@ -26,6 +25,10 @@ class ShellModule(ShellBase):
|
||||
# Family of shells this has. Must match the filename without extension
|
||||
SHELL_FAMILY = 'csh'
|
||||
|
||||
# commonly used
|
||||
ECHO = 'echo'
|
||||
COMMAND_SEP = ';'
|
||||
|
||||
# How to end lines in a python script one-liner
|
||||
_SHELL_EMBEDDED_PY_EOL = '\\\n'
|
||||
_SHELL_REDIRECT_ALLNULL = '>& /dev/null'
|
||||
|
||||
@@ -4,13 +4,8 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.six import text_type
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.plugins.shell.sh import ShellModule as ShModule
|
||||
|
||||
DOCUMENTATION = '''
|
||||
name: fish
|
||||
plugin_type: shell
|
||||
short_description: fish shell (/bin/fish)
|
||||
description:
|
||||
- This is here because some people are restricted to fish.
|
||||
@@ -18,6 +13,10 @@ DOCUMENTATION = '''
|
||||
- shell_common
|
||||
'''
|
||||
|
||||
from ansible.module_utils.six import text_type
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.plugins.shell.sh import ShellModule as ShModule
|
||||
|
||||
|
||||
class ShellModule(ShModule):
|
||||
|
||||
|
||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
@@ -1,3 +1,4 @@
|
||||
---
|
||||
language: python
|
||||
|
||||
env:
|
||||
@@ -8,56 +9,55 @@ matrix:
|
||||
exclude:
|
||||
- env: T=none
|
||||
include:
|
||||
- env: T=sanity/1
|
||||
- env: T=sanity/2
|
||||
- env: T=sanity/3
|
||||
- env: T=sanity/4
|
||||
- env: T=sanity/5
|
||||
|
||||
- env: T=units/2.6/1
|
||||
- env: T=units/2.7/1
|
||||
- env: T=units/3.5/1
|
||||
- env: T=units/3.6/1
|
||||
- env: T=units/3.7/1
|
||||
- env: T=units/3.8/1
|
||||
- env: T=units/3.9/1
|
||||
|
||||
- env: T=units/2.6/2
|
||||
- env: T=units/2.7/2
|
||||
- env: T=units/3.5/2
|
||||
- env: T=units/3.6/2
|
||||
- env: T=units/3.7/2
|
||||
- env: T=units/3.8/2
|
||||
- env: T=units/3.9/2
|
||||
|
||||
- env: T=units/2.6/3
|
||||
- env: T=units/2.7/3
|
||||
- env: T=units/3.5/3
|
||||
- env: T=units/3.6/3
|
||||
- env: T=units/3.7/3
|
||||
- env: T=units/3.8/3
|
||||
- env: T=units/3.9/3
|
||||
|
||||
- env: T=aix/7.2/1
|
||||
- env: T=osx/10.11/1
|
||||
- env: T=rhel/7.6/1
|
||||
- env: T=rhel/8.1/1
|
||||
- env: T=freebsd/11.1/1
|
||||
- env: T=freebsd/12.1/1
|
||||
- env: T=linux/centos6/1
|
||||
- env: T=linux/centos7/1
|
||||
- env: T=linux/centos8/1
|
||||
- env: T=linux/fedora30/1
|
||||
- env: T=linux/fedora31/1
|
||||
- env: T=linux/opensuse15py2/1
|
||||
- env: T=linux/opensuse15/1
|
||||
- env: T=linux/ubuntu1604/1
|
||||
- env: T=linux/ubuntu1804/1
|
||||
- env: T=2.9/osx/10.11/1
|
||||
- env: T=2.9/rhel/7.6/1
|
||||
- env: T=2.9/rhel/8.1/1
|
||||
- env: T=2.9/freebsd/11.1/1
|
||||
- env: T=2.9/freebsd/12.0/1
|
||||
- env: T=2.9/linux/centos6/1
|
||||
- env: T=2.9/linux/centos7/1
|
||||
# - env: T=2.9/linux/centos8/1
|
||||
- env: T=2.9/linux/fedora30/1
|
||||
- env: T=2.9/linux/fedora31/1
|
||||
- env: T=2.9/linux/opensuse15py2/1
|
||||
- env: T=2.9/linux/opensuse15/1
|
||||
- env: T=2.9/linux/ubuntu1604/1
|
||||
- env: T=2.9/linux/ubuntu1804/1
|
||||
# - env: T=2.10/aix/7.2/1
|
||||
- env: T=2.10/osx/10.11/1
|
||||
- env: T=2.10/rhel/7.6/1
|
||||
- env: T=2.10/rhel/8.2/1
|
||||
- env: T=2.10/freebsd/11.1/1
|
||||
- env: T=2.10/freebsd/12.1/1
|
||||
- env: T=2.10/linux/centos6/1
|
||||
- env: T=2.10/linux/centos7/1
|
||||
# - env: T=2.10/linux/centos8/1
|
||||
- env: T=2.10/linux/fedora30/1
|
||||
- env: T=2.10/linux/fedora31/1
|
||||
- env: T=2.10/linux/opensuse15py2/1
|
||||
- env: T=2.10/linux/opensuse15/1
|
||||
- env: T=2.10/linux/ubuntu1604/1
|
||||
- env: T=2.10/linux/ubuntu1804/1
|
||||
# - env: T=devel/aix/7.2/1
|
||||
- env: T=devel/osx/10.11/1
|
||||
- env: T=devel/rhel/7.6/1
|
||||
- env: T=devel/rhel/8.1/1
|
||||
- env: T=devel/freebsd/11.1/1
|
||||
- env: T=devel/freebsd/12.1/1
|
||||
- env: T=devel/linux/centos6/1
|
||||
- env: T=devel/linux/centos7/1
|
||||
# - env: T=devel/linux/centos8/1
|
||||
- env: T=devel/linux/fedora30/1
|
||||
- env: T=devel/linux/fedora31/1
|
||||
- env: T=devel/linux/opensuse15py2/1
|
||||
- env: T=devel/linux/opensuse15/1
|
||||
- env: T=devel/linux/ubuntu1604/1
|
||||
- env: T=devel/linux/ubuntu1804/1
|
||||
|
||||
branches:
|
||||
except:
|
||||
- "*-patch-*"
|
||||
- "revert-*-*"
|
||||
- revert-*-*
|
||||
|
||||
build:
|
||||
ci:
|
||||
|
||||
2
test-requirements.txt
Normal file
2
test-requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
coverage==4.5.4
|
||||
pytest-xdist
|
||||
3
tests/integration/requirements.yml
Normal file
3
tests/integration/requirements.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
collections:
|
||||
- community.general
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# (c) 2017, Martin Krizek <mkrizek@redhat.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
@@ -16,25 +17,44 @@
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Create ansible user
|
||||
user:
|
||||
ansible.builtin.user:
|
||||
name: "{{ test_user }}"
|
||||
|
||||
- name: Create ansible group
|
||||
group:
|
||||
ansible.builtin.group:
|
||||
name: "{{ test_group }}"
|
||||
|
||||
- name: Clean up working directory and files
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir }}"
|
||||
state: absent
|
||||
|
||||
- name: Create working directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Create ansible file
|
||||
file:
|
||||
ansible.builtin.file:
|
||||
path: "{{ test_file }}"
|
||||
state: touch
|
||||
mode: "0644"
|
||||
|
||||
- name: Create ansible dir
|
||||
file:
|
||||
ansible.builtin.file:
|
||||
path: "{{ test_dir }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
- name: Install acl package
|
||||
ansible.builtin.package:
|
||||
name: acl
|
||||
state: present
|
||||
|
||||
##############################################################################
|
||||
- name: Grant ansible user read access to a file
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_file }}"
|
||||
entity: "{{ test_user }}"
|
||||
etype: user
|
||||
@@ -42,12 +62,21 @@
|
||||
state: present
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_file | quote }}"
|
||||
- name: Debug ansible.posix.acl output
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ output }}"
|
||||
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_file | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Debug getfacl output
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ getfacl_output.stdout_lines }}"
|
||||
|
||||
- name: Verify Output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is changed
|
||||
- output is not failed
|
||||
@@ -55,16 +84,25 @@
|
||||
- "'user:{{ test_user }}:r--' in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
- name: Obtain the acl for a specific file
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_file }}"
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_file | quote }}"
|
||||
- name: Debug ansible.posix.acl output
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ output }}"
|
||||
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_file | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Debug getfacl output
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ getfacl_output.stdout_lines }}"
|
||||
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is not changed
|
||||
- output is not failed
|
||||
@@ -79,20 +117,22 @@
|
||||
- "'mask::r--' in getfacl_output.stdout_lines"
|
||||
- "'other::r--' in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
#
|
||||
- name: Removes the acl for ansible user on a specific file
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_file }}"
|
||||
entity: "{{ test_user }}"
|
||||
etype: user
|
||||
state: absent
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_file | quote }}"
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_file | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is changed
|
||||
- output is not failed
|
||||
@@ -100,21 +140,22 @@
|
||||
- "'user:{{ test_user }}:r--' not in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
- name: Sets default acl for ansible user on ansible dir
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_dir }}"
|
||||
entity: "{{ test_user }}"
|
||||
etype: user
|
||||
permissions: rw
|
||||
default: yes
|
||||
default: true
|
||||
state: present
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_dir | quote }}"
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is changed
|
||||
- output is not failed
|
||||
@@ -122,22 +163,24 @@
|
||||
- "'default:user:{{ test_user }}:rw-' in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
- name: Cleanup
|
||||
shell: "setfacl -b {{ test_dir | quote }}"
|
||||
ansible.builtin.command: setfacl -b {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
##############################################################################
|
||||
- name: Same as previous but using entry shorthand
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_dir }}"
|
||||
entry: "user:{{ test_user }}:rw-"
|
||||
default: yes
|
||||
entry: user:{{ test_user }}:rw-
|
||||
default: true
|
||||
state: present
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_dir | quote }}"
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is changed
|
||||
- output is not failed
|
||||
@@ -145,19 +188,20 @@
|
||||
- "'default:user:{{ test_user }}:rw-' in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
- name: Same as previous, to test idempotence
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_dir }}"
|
||||
entry: "user:{{ test_user }}:rw-"
|
||||
default: yes
|
||||
entry: user:{{ test_user }}:rw-
|
||||
default: true
|
||||
state: present
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_dir | quote }}"
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is not changed
|
||||
- output is not failed
|
||||
@@ -165,32 +209,34 @@
|
||||
- "'default:user:{{ test_user }}:rw-' in getfacl_output.stdout_lines"
|
||||
##############################################################################
|
||||
- name: Cleanup
|
||||
shell: "setfacl -b {{ test_dir | quote }}"
|
||||
ansible.builtin.command: setfacl -b {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
##############################################################################
|
||||
- name: Set default acls
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_dir }}"
|
||||
entry: "{{ item }}"
|
||||
default: yes
|
||||
default: true
|
||||
state: present
|
||||
with_items:
|
||||
- "user:{{ test_user }}:rw-"
|
||||
- "group:{{ test_group }}:rw-"
|
||||
- user:{{ test_user }}:rw-
|
||||
- group:{{ test_group }}:rw-
|
||||
|
||||
- name: Remove default group test_user acl
|
||||
acl:
|
||||
ansible.posix.acl:
|
||||
path: "{{ test_dir }}"
|
||||
entry: "group:{{ test_group }}:rw-"
|
||||
default: yes
|
||||
entry: group:{{ test_group }}:rw-
|
||||
default: true
|
||||
state: absent
|
||||
register: output
|
||||
|
||||
- name: get getfacl output
|
||||
shell: "getfacl {{ test_dir | quote }}"
|
||||
- name: Get getfacl output
|
||||
ansible.builtin.command: getfacl {{ test_dir | quote }}
|
||||
changed_when: false
|
||||
register: getfacl_output
|
||||
|
||||
- name: verify output
|
||||
assert:
|
||||
- name: Verify output
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- output is changed
|
||||
- output is not failed
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# (c) 2017, Martin Krizek <mkrizek@redhat.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
@@ -15,22 +16,21 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- block:
|
||||
|
||||
- include: acl.yml
|
||||
when: ansible_system == 'Linux' # TODO enable acls mount option on FreeBSD to test it there too
|
||||
|
||||
always:
|
||||
- name: delete created directory and file
|
||||
file:
|
||||
path: '{{ item }}'
|
||||
state: absent
|
||||
with_items:
|
||||
- '{{ test_dir }}'
|
||||
- '{{ test_file }}'
|
||||
|
||||
- name: Test ACL
|
||||
vars:
|
||||
test_user: ansible_user
|
||||
test_group: ansible_group
|
||||
test_file: '{{ output_dir }}/ansible file'
|
||||
test_file: "{{ output_dir }}/ansible file"
|
||||
test_dir: "{{ output_dir }}/ansible_dir/with some space"
|
||||
block:
|
||||
- name: Include tests task file
|
||||
ansible.builtin.include_tasks: acl.yml
|
||||
when: ansible_system == 'Linux' # TODO enable acls mount option on FreeBSD to test it there too
|
||||
always:
|
||||
- name: Delete created directory and file
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
with_items:
|
||||
- "{{ test_dir }}"
|
||||
- "{{ test_file }}"
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- prepare_tests
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
# Test code for the at module.
|
||||
# (c) 2017, James Tanner <tanner.jc@gmail.com>
|
||||
|
||||
@@ -16,47 +17,56 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- set_fact: output_dir_test={{output_dir}}/at
|
||||
- name: Set output_dir_test fast
|
||||
ansible.builtin.set_fact:
|
||||
output_dir_test: "{{ output_dir }}/at"
|
||||
|
||||
- name: make sure our testing sub-directory does not exist
|
||||
file: path="{{ output_dir_test }}" state=absent
|
||||
- name: Make sure our testing sub-directory does not exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir_test }}"
|
||||
state: absent
|
||||
|
||||
- name: create our testing sub-directory
|
||||
file: path="{{ output_dir_test }}" state=directory
|
||||
- name: Create our testing sub-directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir_test }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
##
|
||||
## at
|
||||
##
|
||||
|
||||
- name: define distros to attempt installing at on
|
||||
set_fact:
|
||||
- name: Define distros to attempt installing at on
|
||||
ansible.builtin.set_fact:
|
||||
package_distros:
|
||||
- RedHat
|
||||
- CentOS
|
||||
- ScientificLinux
|
||||
- Fedora
|
||||
- Ubuntu
|
||||
- Debian
|
||||
- openSUSE Leap
|
||||
- RedHat
|
||||
- CentOS
|
||||
- ScientificLinux
|
||||
- Fedora
|
||||
- Ubuntu
|
||||
- Debian
|
||||
- openSUSE Leap
|
||||
|
||||
- name: ensure at is installed
|
||||
package:
|
||||
- name: Ensure at is installed
|
||||
ansible.builtin.package:
|
||||
name: at
|
||||
state: present
|
||||
when: ansible_distribution in package_distros
|
||||
|
||||
- name: run the first example
|
||||
at:
|
||||
command: "ls -d / > /dev/null"
|
||||
- name: Run the first example
|
||||
ansible.posix.at:
|
||||
command: ls -d / > /dev/null
|
||||
count: 20
|
||||
units: minutes
|
||||
register: at_test0
|
||||
- debug: var=at_test0
|
||||
- name: validate results
|
||||
assert:
|
||||
that:
|
||||
- 'at_test0.changed is defined'
|
||||
- 'at_test0.count is defined'
|
||||
- 'at_test0.script_file is defined'
|
||||
- 'at_test0.state is defined'
|
||||
- 'at_test0.units is defined'
|
||||
- name: Debug var=at_test0
|
||||
ansible.builtin.debug:
|
||||
var: at_test0
|
||||
- name: Validate results
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- at_test0.changed is defined
|
||||
- at_test0.count is defined
|
||||
- at_test0.script_file is defined
|
||||
- at_test0.state is defined
|
||||
- at_test0.units is defined
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---
|
||||
dss_key_basic: ssh-dss DATA_BASIC root@testing
|
||||
dss_key_unquoted_option: idle-timeout=5m ssh-dss DATA_UNQUOTED_OPTION root@testing
|
||||
dss_key_command: command="/bin/true" ssh-dss DATA_COMMAND root@testing
|
||||
@@ -8,29 +9,31 @@ dss_key_trailing: ssh-dss DATA_TRAILING root@testing foo bar baz
|
||||
rsa_key_basic: ssh-rsa DATA_BASIC root@testing
|
||||
|
||||
multiple_key_base: |
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
|
||||
multiple_key_different_order: |
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
|
||||
multiple_key_different_order_2: |
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-rsa WHATEVER 2.5@testing
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
ssh-dss DATA_TRAILING 2@testing foo bar baz
|
||||
ssh-rsa WHATEVER 2.5@testing
|
||||
ssh-dss DATA_TRAILING 3@testing foo bar baz
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
|
||||
multiple_key_exclusive: |
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
|
||||
multiple_keys_comments: |
|
||||
ssh-rsa DATA_BASIC 1@testing
|
||||
# I like adding comments yo-dude-this-is-not-a-key INVALID_DATA 2@testing
|
||||
ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
|
||||
key_path: /tmp/id_rsa.pub
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- prepare_tests
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
# -------------------------------------------------------------
|
||||
# check mode
|
||||
|
||||
- name: CHECK MODE | copy an existing file in place with comments
|
||||
ansible.builtin.copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
mode: "0600"
|
||||
|
||||
- name: CHECK MODE | add key in check mode to validate return codes
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order_2 }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
check_mode: true
|
||||
register: result
|
||||
|
||||
- name: CHECK MODE | assert that authorized_keys return values are consistent
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- '"user" in result'
|
||||
- '"key" in result'
|
||||
|
||||
- name: CHECK MODE | recopy authorized_keys to ensure it was not changed
|
||||
ansible.builtin.copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
mode: "0600"
|
||||
register: result
|
||||
|
||||
- name: CHECK MODE | assert that the authorized_keys file was not changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
- name: Create key file for test
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ key_path }}"
|
||||
content: "{{ rsa_key_basic }}"
|
||||
mode: "0600"
|
||||
|
||||
- name: Add key using path
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: file://{{ key_path }}
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
|
||||
- name: Add key using path again
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: file://{{ key_path }}
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that no changes were applied
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
52
tests/integration/targets/authorized_key/tasks/comments.yml
Normal file
52
tests/integration/targets/authorized_key/tasks/comments.yml
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
# -------------------------------------------------------------
|
||||
# comments
|
||||
|
||||
- name: Add rsa key with existing comment
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Change the comment on an existing key
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
comment: user@acme.com
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Get the file content
|
||||
ansible.builtin.command: fgrep DATA_BASIC "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: false
|
||||
register: content
|
||||
|
||||
- name: Assert that comment on an existing key was changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "'user@acme.com' in content.stdout"
|
||||
|
||||
- name: Set the same key with comment to ensure no changes are reported
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
comment: user@acme.com
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that no changes were made when running again
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not result.changed
|
||||
|
||||
- name: Debug the result and content
|
||||
ansible.builtin.debug:
|
||||
var: "{{ item }}"
|
||||
verbosity: 1
|
||||
with_items:
|
||||
- result
|
||||
- content
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
# test code for the authorized_key module
|
||||
# (c) 2014, James Cammarata <jcammarata@ansible.com>
|
||||
|
||||
# - (c) 2014, James Cammarata <jcammarata@ansible.com>
|
||||
# - (c) 2021, Hideki Saito <saito@fgrep.org>
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
@@ -16,470 +17,20 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Setup testing environment
|
||||
ansible.builtin.import_tasks: setup_steps.yml
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Setup steps
|
||||
- name: Test for multiple keys handling
|
||||
ansible.builtin.import_tasks: multiple_keys.yml
|
||||
|
||||
- name: Test for ssh-dss key handling
|
||||
ansible.builtin.import_tasks: ssh_dss.yml
|
||||
|
||||
- name: copy an existing file in place with comments
|
||||
copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
- name: Test for check mode
|
||||
ansible.builtin.import_tasks: check_mode.yml
|
||||
|
||||
- name: add multiple keys different order
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order_2 }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
- name: Test for the management of comments with key
|
||||
ansible.builtin.import_tasks: comments.yml
|
||||
|
||||
- name: get the file content
|
||||
shell: cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: no
|
||||
register: multiple_keys_existing
|
||||
|
||||
- name: assert that the key was added and comments and ordering preserved
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- '"# I like candy" in multiple_keys_existing.stdout'
|
||||
- '"# I like candy" in multiple_keys_existing.stdout_lines[0]'
|
||||
- '"ssh-rsa DATA_BASIC 1@testing" in multiple_keys_existing.stdout'
|
||||
# The specific index is a little fragile, but I want to verify the line shows up
|
||||
# as the 3rd line in the new entries after the existing entries and comments are preserved
|
||||
- '"ssh-rsa DATA_BASIC 1@testing" in multiple_keys_existing.stdout_lines[7]'
|
||||
|
||||
# start afresh
|
||||
|
||||
- name: remove file foo.txt
|
||||
file:
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
state: absent
|
||||
|
||||
- name: touch the authorized_keys file
|
||||
file:
|
||||
dest: "{{ output_dir }}/authorized_keys"
|
||||
state: touch
|
||||
register: result
|
||||
|
||||
- name: assert that the authorized_keys file was created
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.state == "file"'
|
||||
|
||||
- name: add multiple keys
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_base }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == multiple_key_base'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: add multiple keys different order
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == multiple_key_different_order'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: add multiple keys exclusive
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_exclusive }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
exclusive: true
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == multiple_key_exclusive'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: add multiple keys in different calls
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "ecdsa-sha2-nistp521 ECDSA_DATA 4@testing"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: add multiple keys in different calls
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "ssh-rsa DATA_BASIC 1@testing"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: get the file content
|
||||
shell: cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: no
|
||||
register: multiple_keys_at_a_time
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == false'
|
||||
- 'multiple_keys_at_a_time.stdout == multiple_key_exclusive.strip()'
|
||||
|
||||
- name: add multiple keys comment
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_keys_comments }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
exclusive: true
|
||||
register: result
|
||||
|
||||
- name: get the file content
|
||||
shell: cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: no
|
||||
register: multiple_keys_comments
|
||||
|
||||
- name: assert that the keys exist and comment only lines were not added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
- 'multiple_keys_comments.stdout == multiple_key_exclusive.strip()'
|
||||
- 'result.key_options == None'
|
||||
|
||||
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# basic ssh-dss key
|
||||
|
||||
- name: add basic ssh-dss key
|
||||
authorized_key: user=root key="{{ dss_key_basic }}" state=present path="{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_basic'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add basic ssh-dss key
|
||||
authorized_key: user=root key="{{ dss_key_basic }}" state=present path="{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with an unquoted option
|
||||
|
||||
- name: add ssh-dss key with an unquoted option
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_unquoted_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_unquoted_option'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with an unquoted option
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_unquoted_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a leading command="/bin/foo"
|
||||
|
||||
- name: add ssh-dss key with a leading command
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_command'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with a leading command
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a complex quoted leading command
|
||||
# ie. command="/bin/echo foo 'bar baz'"
|
||||
|
||||
- name: add ssh-dss key with a complex quoted leading command
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_complex_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_complex_command'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with a complex quoted leading command
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_complex_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a command and a single option, which are
|
||||
# in a comma-separated list
|
||||
|
||||
- name: add ssh-dss key with a command and a single option
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_single_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_command_single_option'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with a command and a single option
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_single_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a command and multiple other options
|
||||
|
||||
- name: add ssh-dss key with a command and multiple options
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_multiple_options }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_command_multiple_options'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with a command and multiple options
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_multiple_options }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with multiple trailing parts, which are space-
|
||||
# separated and not quoted in any way
|
||||
|
||||
- name: add ssh-dss key with trailing parts
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_trailing }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_trailing'
|
||||
- 'result.key_options == None'
|
||||
|
||||
- name: re-add ssh-dss key with trailing parts
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_trailing }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that nothing changed
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# basic ssh-dss key with mutliple permit-open options
|
||||
# https://github.com/ansible/ansible-modules-core/issues/1715
|
||||
|
||||
- name: add basic ssh-dss key with multi-opts
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_basic }}"
|
||||
key_options: 'no-agent-forwarding,no-X11-forwarding,permitopen="10.9.8.1:8080",permitopen="10.9.8.1:9001"'
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: assert that the key with multi-opts was added
|
||||
assert:
|
||||
that:
|
||||
- 'result.changed == True'
|
||||
- 'result.key == dss_key_basic'
|
||||
- 'result.key_options == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\""'
|
||||
|
||||
- name: get the file content
|
||||
shell: cat "{{ output_dir | expanduser }}/authorized_keys" | fgrep DATA_BASIC
|
||||
changed_when: no
|
||||
register: content
|
||||
|
||||
- name: validate content
|
||||
assert:
|
||||
that:
|
||||
- 'content.stdout == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\" ssh-dss DATA_BASIC root@testing"'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# check mode
|
||||
|
||||
- name: copy an existing file in place with comments
|
||||
copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
|
||||
- authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order_2 }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
check_mode: True
|
||||
register: result
|
||||
|
||||
- name: assert that the file was not changed
|
||||
copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- 'result.changed == False'
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# comments
|
||||
|
||||
- name: Add rsa key with existing comment
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Change the comment on an existing key
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
comment: user@acme.com
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: get the file content
|
||||
shell: cat "{{ output_dir | expanduser }}/authorized_keys" | fgrep DATA_BASIC
|
||||
changed_when: no
|
||||
register: content
|
||||
|
||||
- name: Assert that comment on an existing key was changed
|
||||
assert:
|
||||
that:
|
||||
- "'user@acme.com' in content.stdout"
|
||||
|
||||
- name: Set the same key with comment to ensure no changes are reported
|
||||
authorized_key:
|
||||
user: root
|
||||
key: "{{ rsa_key_basic }}"
|
||||
comment: user@acme.com
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that no changes were made when running again
|
||||
assert:
|
||||
that:
|
||||
- not result.changed
|
||||
|
||||
- debug:
|
||||
var: "{{ item }}"
|
||||
verbosity: 1
|
||||
with_items:
|
||||
- result
|
||||
- content
|
||||
- name: Test for specifying key as a path
|
||||
ansible.builtin.import_tasks: check_path.yml
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
# -------------------------------------------------------------
|
||||
# multiple keys
|
||||
|
||||
- name: Add multiple keys
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_base }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == multiple_key_base
|
||||
- result.key_options == None
|
||||
|
||||
- name: Add multiple keys different order
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == multiple_key_different_order
|
||||
- result.key_options == None
|
||||
|
||||
- name: Add multiple keys exclusive
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_exclusive }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
exclusive: true
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == multiple_key_exclusive
|
||||
- result.key_options == None
|
||||
|
||||
- name: Add multiple keys in different calls
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: ecdsa-sha2-nistp521 ECDSA_DATA 4@testing
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Add multiple keys in different calls
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: ssh-rsa DATA_BASIC 1@testing
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Get the file content
|
||||
ansible.builtin.command: /bin/cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: false
|
||||
register: multiple_keys_at_a_time
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- multiple_keys_at_a_time.stdout == multiple_key_exclusive.strip()
|
||||
|
||||
- name: Add multiple keys comment
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_keys_comments }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
exclusive: true
|
||||
register: result
|
||||
|
||||
- name: Get the file content
|
||||
ansible.builtin.command: /bin/cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: false
|
||||
register: multiple_keys_comments
|
||||
|
||||
- name: Assert that the keys exist and comment only lines were not added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
- multiple_keys_comments.stdout == multiple_key_exclusive.strip()
|
||||
- result.key_options == None
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
# -------------------------------------------------------------
|
||||
# Setup steps
|
||||
- name: Clean up the working directory and files
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir }}"
|
||||
state: absent
|
||||
|
||||
- name: Create the working directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir }}"
|
||||
state: directory
|
||||
mode: "0744"
|
||||
|
||||
- name: Copy an existing file in place with comments
|
||||
ansible.builtin.copy:
|
||||
src: existing_authorized_keys
|
||||
dest: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
mode: "0600"
|
||||
|
||||
- name: Add multiple keys different order
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ multiple_key_different_order_2 }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Get the file content
|
||||
ansible.builtin.command: /bin/cat "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: false
|
||||
register: multiple_keys_existing
|
||||
|
||||
- name: Assert that the key was added and comments and ordering preserved
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- '"# I like candy" in multiple_keys_existing.stdout'
|
||||
- '"# I like candy" in multiple_keys_existing.stdout_lines[0]'
|
||||
- '"ssh-rsa DATA_BASIC 1@testing" in multiple_keys_existing.stdout'
|
||||
# The specific index is a little fragile, but I want to verify the line shows up
|
||||
# as the 3rd line in the new entries after the existing entries and comments are preserved
|
||||
- '"ssh-rsa DATA_BASIC 1@testing" in multiple_keys_existing.stdout_lines[7]'
|
||||
|
||||
# start afresh
|
||||
|
||||
- name: Remove file foo.txt
|
||||
ansible.builtin.file:
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
state: absent
|
||||
|
||||
- name: Touch the authorized_keys file
|
||||
ansible.builtin.file:
|
||||
dest: "{{ output_dir }}/authorized_keys"
|
||||
state: touch
|
||||
mode: "0600"
|
||||
register: result
|
||||
|
||||
- name: Assert that the authorized_keys file was created
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.state == "file"
|
||||
250
tests/integration/targets/authorized_key/tasks/ssh_dss.yml
Normal file
250
tests/integration/targets/authorized_key/tasks/ssh_dss.yml
Normal file
@@ -0,0 +1,250 @@
|
||||
---
|
||||
# -------------------------------------------------------------
|
||||
# basic ssh-dss key
|
||||
|
||||
- name: Add basic ssh-dss key
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_basic }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_basic
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add basic ssh-dss key
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_basic }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with an unquoted option
|
||||
|
||||
- name: Add ssh-dss key with an unquoted option
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_unquoted_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_unquoted_option
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with an unquoted option
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_unquoted_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a leading command="/bin/foo"
|
||||
|
||||
- name: Add ssh-dss key with a leading command
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_command
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with a leading command
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a complex quoted leading command
|
||||
# ie. command="/bin/echo foo 'bar baz'"
|
||||
|
||||
- name: Add ssh-dss key with a complex quoted leading command
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_complex_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_complex_command
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with a complex quoted leading command
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_complex_command }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a command and a single option, which are
|
||||
# in a comma-separated list
|
||||
|
||||
- name: Add ssh-dss key with a command and a single option
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_single_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_command_single_option
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with a command and a single option
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_single_option }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with a command and multiple other options
|
||||
|
||||
- name: Add ssh-dss key with a command and multiple options
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_multiple_options }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_command_multiple_options
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with a command and multiple options
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_command_multiple_options }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# ssh-dss key with multiple trailing parts, which are space-
|
||||
# separated and not quoted in any way
|
||||
|
||||
- name: Add ssh-dss key with trailing parts
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_trailing }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_trailing
|
||||
- result.key_options == None
|
||||
|
||||
- name: Re-add ssh-dss key with trailing parts
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_trailing }}"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that nothing changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == False
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# basic ssh-dss key with mutliple permit-open options
|
||||
# https://github.com/ansible/ansible-modules-core/issues/1715
|
||||
|
||||
- name: Add basic ssh-dss key with multi-opts
|
||||
ansible.posix.authorized_key:
|
||||
user: root
|
||||
key: "{{ dss_key_basic }}"
|
||||
key_options: no-agent-forwarding,no-X11-forwarding,permitopen="10.9.8.1:8080",permitopen="10.9.8.1:9001"
|
||||
state: present
|
||||
path: "{{ output_dir | expanduser }}/authorized_keys"
|
||||
register: result
|
||||
|
||||
- name: Assert that the key with multi-opts was added
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result.changed == True
|
||||
- result.key == dss_key_basic
|
||||
- result.key_options == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\""
|
||||
|
||||
- name: Get the file content
|
||||
ansible.builtin.command: fgrep DATA_BASIC "{{ output_dir | expanduser }}/authorized_keys"
|
||||
changed_when: false
|
||||
register: content
|
||||
|
||||
- name: Validate content
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- content.stdout == "no-agent-forwarding,no-X11-forwarding,permitopen=\"10.9.8.1:8080\",permitopen=\"10.9.8.1:9001\" ssh-dss DATA_BASIC root@testing"
|
||||
7
tests/integration/targets/firewalld/aliases
Normal file
7
tests/integration/targets/firewalld/aliases
Normal file
@@ -0,0 +1,7 @@
|
||||
needs/privileged
|
||||
needs/root
|
||||
destructive
|
||||
shippable/posix/group1
|
||||
skip/aix
|
||||
skip/freebsd
|
||||
skip/osx
|
||||
3
tests/integration/targets/firewalld/meta/main.yml
Normal file
3
tests/integration/targets/firewalld/meta/main.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- setup_pkg_mgr
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - icmp block inversion operations
|
||||
# (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Icmp block inversion enabled when icmp block inversion is truthy and state is enabled
|
||||
block:
|
||||
- name: Testing enable icmp block inversion
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing enable icmp block inversion (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is enabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Icmp block inversion disabled when icmp block inversion is falsy and state is enabled
|
||||
block:
|
||||
- name: Testing disable icmp block inversion
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is disabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing disable icmp block inversion (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is disabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Icmp block inversion enabled when icmp block inversion is falsy and state is disabled
|
||||
block:
|
||||
- name: Testing enable icmp block inversion
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: false
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing enable icmp block inversion (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: false
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is enabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Icmp block inversion disabled when icmp block inversion is truthy and state is disabled
|
||||
block:
|
||||
- name: Testing disable icmp block inversion
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: true
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is disabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing disable icmp block inversion (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
icmp_block_inversion: true
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert icmp block inversion is disabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - interface operations
|
||||
# (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Validate adding interface
|
||||
block:
|
||||
- name: Add lo interface to trusted zone
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: trusted
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo was added to trusted zone
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Add lo interface to trusted zone (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: trusted
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo was added to trusted zone (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Validate moving interfaces
|
||||
block:
|
||||
- name: Move lo interface from trusted zone to internal zone
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo was moved from trusted zone to internal zone
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Move lo interface from trusted zone to internal zone (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo was moved from trusted zone to internal zone (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Validate removing interface
|
||||
block:
|
||||
- name: Remove lo interface from internal zone
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo interface was removed from internal zone
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Remove lo interface from internal zone (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
interface: lo
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert lo interface was removed from internal zone (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
45
tests/integration/targets/firewalld/tasks/main.yml
Normal file
45
tests/integration/targets/firewalld/tasks/main.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
# Test playbook for the firewalld module
|
||||
# (c) 2017, Adam Miller <admiller@redhat.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Run firewalld tests
|
||||
when:
|
||||
- ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('7', '>=')
|
||||
- not (ansible_distribution == "Ubuntu" and ansible_distribution_version is version('14.04', '=='))
|
||||
# Firewalld package on OpenSUSE (15+) require Python 3, so we skip on OpenSUSE running py2 on these newer distros
|
||||
- not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and ansible_python.version.major != 3)
|
||||
- not (ansible_facts.distribution == "CentOS" and ansible_distribution_major_version is version('7', '==')) # FIXME
|
||||
block:
|
||||
- name: Ensure firewalld is installed
|
||||
ansible.builtin.package:
|
||||
name: firewalld
|
||||
state: present
|
||||
# This doesn't work for CentOS 6 because firewalld doesn't exist in CentOS6
|
||||
|
||||
- name: Enable dbus-broker daemon
|
||||
ansible.builtin.service:
|
||||
name: dbus-broker
|
||||
enabled: true
|
||||
state: started
|
||||
when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('34', '=='))
|
||||
|
||||
- name: Test Online Operations
|
||||
block:
|
||||
- name: Start firewalld
|
||||
ansible.builtin.service:
|
||||
name: firewalld
|
||||
state: started
|
||||
|
||||
- name: Import test tasks
|
||||
ansible.builtin.import_tasks: run_all_tests.yml
|
||||
|
||||
- name: Test Offline Operations
|
||||
block:
|
||||
- name: Stop firewalld
|
||||
ansible.builtin.service:
|
||||
name: firewalld
|
||||
state: stopped
|
||||
|
||||
- name: Import test tasks
|
||||
ansible.builtin.import_tasks: run_all_tests.yml
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - masquerade operations
|
||||
# (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Masquerade enabled when masquerade is truthy and state is enabled
|
||||
block:
|
||||
- name: Testing enable masquerade
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing enable masquerade (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is enabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Masquerade disabled when masquerade is falsy and state is enabled
|
||||
block:
|
||||
- name: Testing disable masquerade
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is disabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing disable masquerade (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is disabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Masquerade enabled when masquerade is falsy and state is disabled
|
||||
block:
|
||||
- name: Testing enable masquerade
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: false
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is enabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing enable masquerade (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: false
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is enabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Masquerade disabled when masquerade is truthy and state is disabled
|
||||
block:
|
||||
- name: Testing disable masquerade
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: true
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is disabled
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Testing disable masquerade (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: trusted
|
||||
masquerade: true
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert masquerade is disabled (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
@@ -0,0 +1,64 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - port operations
|
||||
# (c) 2017, Adam Miller <admiller@redhat.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Firewalld port forward test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
port_forward:
|
||||
- port: 8080
|
||||
proto: tcp
|
||||
toport: 8081
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
port_forward:
|
||||
- port: 8080
|
||||
proto: tcp
|
||||
toport: 8081
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld port test permanent disabled
|
||||
ansible.posix.firewalld:
|
||||
port_forward:
|
||||
- port: 8080
|
||||
proto: tcp
|
||||
toport: 8081
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent disabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port test permanent disabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
port_forward:
|
||||
- port: 8080
|
||||
proto: tcp
|
||||
toport: 8081
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent disabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
109
tests/integration/targets/firewalld/tasks/port_test_cases.yml
Normal file
109
tests/integration/targets/firewalld/tasks/port_test_cases.yml
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - port operations
|
||||
# (c) 2017, Adam Miller <admiller@redhat.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Firewalld port range test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
port: 5500-6850/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port range test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port range test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
port: 5500-6850/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port range test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld port test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
port: 6900/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
port: 6900/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld port test disabled
|
||||
ansible.posix.firewalld:
|
||||
port: "{{ item }}"
|
||||
permanent: true
|
||||
state: disabled
|
||||
loop:
|
||||
- 6900/tcp
|
||||
- 5500-6850/tcp
|
||||
|
||||
- name: Firewalld port test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
port: 8081/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
port: 8081/tcp
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld port test permanent disabled
|
||||
ansible.posix.firewalld:
|
||||
port: 8081/tcp
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent disabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld port test permanent disabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
port: 8081/tcp
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld port test permanent disabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - protocol operations
|
||||
# (c) 2022, Robért S. Guhr <rguhr@cronon.net>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Firewalld protocol test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
protocol: ospf
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld protocol test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld protocol test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
protocol: ospf
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld protocol test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld protocol test permanent disabled
|
||||
ansible.posix.firewalld:
|
||||
protocol: ospf
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld protocol test permanent disabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld protocol test permanent disabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
protocol: ospf
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld protocol test permanent disabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
50
tests/integration/targets/firewalld/tasks/run_all_tests.yml
Normal file
50
tests/integration/targets/firewalld/tasks/run_all_tests.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
# Test playbook for the firewalld module
|
||||
# (c) 2017, Adam Miller <admiller@redhat.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Ensure /run/firewalld exists
|
||||
ansible.builtin.file:
|
||||
path: /run/firewalld
|
||||
state: directory
|
||||
mode: "0755"
|
||||
|
||||
# firewalld service operation test cases
|
||||
- name: Include service test cases for firewalld module
|
||||
ansible.builtin.include_tasks: service_test_cases.yml
|
||||
|
||||
# firewalld protocol operation test cases
|
||||
- name: Include protocol test cases for firewalld module
|
||||
ansible.builtin.include_tasks: protocol_test_cases.yml
|
||||
|
||||
# firewalld port operation test cases
|
||||
- name: Include port test cases for firewalld module
|
||||
ansible.builtin.include_tasks: port_test_cases.yml
|
||||
|
||||
# firewalld source operation test cases
|
||||
- name: Include source test cases for firewalld module
|
||||
ansible.builtin.include_tasks: source_test_cases.yml
|
||||
|
||||
# firewalld zone operation test cases
|
||||
- name: Include zone test cases for firewalld module
|
||||
ansible.builtin.include_tasks: zone_test_cases.yml
|
||||
|
||||
# firewalld zone target operation test cases
|
||||
- name: Include zone target test cases for firewalld module
|
||||
ansible.builtin.include_tasks: zone_target_test_cases.yml
|
||||
|
||||
# firewalld port forwarding operation test cases
|
||||
- name: Include port forward target test cases for firewalld module
|
||||
ansible.builtin.include_tasks: port_forward_test_cases.yml
|
||||
|
||||
# firewalld masquerade operation test cases
|
||||
- name: Include masquerade target test cases for firewalld module
|
||||
ansible.builtin.include_tasks: masquerade_test_cases.yml
|
||||
|
||||
# firewalld icmp block inversion operation test cases
|
||||
- name: Include icmp block inversion target test cases for firewalld module
|
||||
ansible.builtin.include_tasks: icmp_block_inversion_test_cases.yml
|
||||
|
||||
# firewalld interface operation test cases
|
||||
- name: Include interface target test cases for firewalld module
|
||||
ansible.builtin.include_tasks: interface_test_cases.yml
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - service operations
|
||||
# (c) 2017, Adam Miller <admiller@redhat.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Firewalld service test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
service: https
|
||||
permanent: true
|
||||
immediate: true
|
||||
offline: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld service test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld service test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
service: https
|
||||
permanent: true
|
||||
immediate: true
|
||||
offline: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld service test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld service test permanent disabled
|
||||
ansible.posix.firewalld:
|
||||
service: https
|
||||
permanent: true
|
||||
immediate: true
|
||||
offline: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld service test permanent disabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld service test permanent disabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
service: https
|
||||
permanent: true
|
||||
immediate: true
|
||||
offline: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld service test permanent disabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - source operations
|
||||
# (c) 2019, Hideki Saito <saito@fgrep.org>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Firewalld source test permanent enabled
|
||||
ansible.posix.firewalld:
|
||||
source: 192.0.2.0/24
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld source test permanent enabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld source test permanent enabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
source: 192.0.2.0/24
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld source test permanent enabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld source test permanent disabled
|
||||
ansible.posix.firewalld:
|
||||
source: 192.0.2.0/24
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld source test permanent disabled worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld source test permanent disabled rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
source: 192.0.2.0/24
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: disabled
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld source test permanent disabled rerun worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld source test permanent enabled is exclusive (verify exclusive error)
|
||||
ansible.posix.firewalld:
|
||||
source: 192.0.2.0/24
|
||||
port: 8081/tcp
|
||||
zone: internal
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: Assert firewalld source test permanent enabled is exclusive (verify exclusive error)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- >
|
||||
result.msg == 'parameters are mutually exclusive:
|
||||
icmp_block|icmp_block_inversion|service|protocol|port|port_forward|rich_rule|interface|forward|masquerade|source|target'
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
# Test playbook for the firewalld module - source operations
|
||||
# (c) 2020, Adam Miller <admiller@redhat.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- name: Firewalld dmz zone target DROP
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: present
|
||||
target: DROP
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target DROP present worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld dmz zone target DROP rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: present
|
||||
target: DROP
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target DROP present worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld dmz zone target DROP absent
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: absent
|
||||
target: DROP
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target DROP absent worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld dmz zone target DROP rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: absent
|
||||
target: DROP
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target DROP present worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld dmz zone target %%REJECT%%
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: present
|
||||
target: "%%REJECT%%"
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target %%REJECT%% present worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld dmz zone target %%REJECT%% rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: present
|
||||
target: "%%REJECT%%"
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target %%REJECT%% present worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Firewalld dmz zone target %%REJECT%% absent
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: absent
|
||||
target: "%%REJECT%%"
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target %%REJECT%% absent worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld dmz zone target %%REJECT%% rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: dmz
|
||||
permanent: true
|
||||
state: absent
|
||||
target: "%%REJECT%%"
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld dmz zone target %%REJECT%% present worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
- name: Firewalld create zone custom
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
permanent: true
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld custom zone created worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld create zone custom rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
permanent: true
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld custom zone created worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Zone forwarding test
|
||||
when: (ansible_distribution == 'RedHat' and ansible_distribution_major_version is version('8', '>='))
|
||||
block:
|
||||
- name: Enable zone forwarding
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
forward: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert zone forwarding is enabled
|
||||
ansible.builtin.debug:
|
||||
var: result is changed
|
||||
|
||||
- name: Enable zone forwarding (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
forward: true
|
||||
permanent: true
|
||||
state: enabled
|
||||
register: result
|
||||
|
||||
- name: Assert zone forwarding is enabled (verify not changed)
|
||||
ansible.builtin.debug:
|
||||
var: result is not changed
|
||||
|
||||
- name: Disable zone forwarding
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
forward: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
|
||||
- name: Assert zone forwarding is disabled
|
||||
ansible.builtin.debug:
|
||||
var: result is changed
|
||||
|
||||
- name: Disable zone forwarding (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
forward: false
|
||||
permanent: true
|
||||
state: enabled
|
||||
|
||||
- name: Assert zone forwarding is disabled (verify not changed)
|
||||
ansible.builtin.debug:
|
||||
var: result is not changed
|
||||
|
||||
- name: Firewalld remove zone custom
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
permanent: true
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld custom zone removed worked
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Firewalld remove custom zone rerun (verify not changed)
|
||||
ansible.posix.firewalld:
|
||||
zone: custom
|
||||
permanent: true
|
||||
state: absent
|
||||
register: result
|
||||
|
||||
- name: Assert firewalld custom zone removed worked (verify not changed)
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- result is not changed
|
||||
5
tests/integration/targets/firewalld_info/aliases
Normal file
5
tests/integration/targets/firewalld_info/aliases
Normal file
@@ -0,0 +1,5 @@
|
||||
destructive
|
||||
shippable/posix/group3
|
||||
skip/aix
|
||||
skip/freebsd
|
||||
skip/osx
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user