230 Commits

Author SHA1 Message Date
Sagi Shnaidman
07374a1f0d Add mandatory requires_ansible version to metadata
Disable ansible-test since it's broken in 2.11[1]

[1] https://github.com/ansible/ansible/issues/75353
Change-Id: Idd2f99ddfe507b5b02de206c1f8c75692d6a84a2
2021-07-29 17:05:11 +03:00
Sagi Shnaidman
8a395a04cf Remove semver check from release job
Jinja in Zuul Ansible version doesn't support semver key for versions.
Change-Id: I8bf9aa17e18f9b6d2c8e307f4368d1cea3362cb5
2021-07-29 10:20:32 +03:00
Zuul
441a61fd8c Merge "Change the pipeline of releasing to tag pipeline" 2021-07-28 23:14:40 +00:00
Sagi Shnaidman
c05b1fdbaf Change the pipeline of releasing to tag pipeline
Because zuul pre-release pipeline is pep400 compliant, but no
semver compliant, we can't push pre-release tags. Galaxy expects
from tags to be pure semver (not pep440).
Change the pipeline to be 'tag' and be triggered by any tag.

Change-Id: Ia288452668179723e35452d6e9579fb1dd0c4c3a
2021-07-28 23:43:51 +03:00
Zuul
9cd92208d6 Merge "Wait for pool to be active and online" 2021-07-28 16:55:56 +00:00
Sagi Shnaidman
5ef192f1f2 Add automatic release job triggered by tag
Currently put it in pre-release pipeline to test that it's ok.

Change-Id: I791478464ed59933273bb0e5f88f3636074c6729
2021-07-28 15:11:07 +03:00
Jesper Schmitz Mouridsen
aed60716ee Wait for pool to be active and online
In order to use a newly generated pool for a new
healthmonitor the pool must be online and active,
to avoid 409 conflict, when trying to update a pool
with e.g pending update as a status.

Change-Id: I160a75c6fbbf1555f3adcb444c77057f59b4cdfb
2021-07-19 13:05:33 +02:00
cuongmax
938b90ea19 Add protocol listener octavia
Signed-off-by: cuongmax <ptitcuongmax@gmail.com>
Change-Id: I98c316865837e7c68fa3eed2f5304950ebfc2141
2021-07-17 09:17:55 +07:00
Zuul
ce853a8f9f Merge "Add missing info to changelog" 2021-06-24 20:42:05 +00:00
Sagi Shnaidman
9911c7f93a Add missing info to changelog
Change-Id: Ic6113a785090cc05771fb2f4730dc25f40a8d4dc
2021-06-24 23:13:24 +03:00
Sagi Shnaidman
8c890e656b Update IRC server in README
Change-Id: Ida13b691ae09c2222ae1c83a88b47702fdf684f4
2021-06-24 16:59:56 +03:00
Sagi Shnaidman
b839f9e25d Bump to devel 1.5.1-dev version
Change-Id: I99857903ed22f46e4bbf317dab49f5e581141ea2
2021-06-23 18:41:14 +03:00
Sagi Shnaidman
b7765776a7 Release 1.5.0 version
Change-Id: I433087510adf633e9656f844680ef22c1aff1a72
2021-06-23 17:41:08 +03:00
Zuul
4490e008c4 Merge "Only apply necessary changes to subnets" 2021-06-23 14:35:49 +00:00
Zuul
f376d4679e Merge "fix update on empty list of allowed address pairs" 2021-06-23 13:10:44 +00:00
Zuul
12ec8c63c2 Merge "Add address scope module" 2021-06-23 11:38:23 +00:00
Jan Hartkopf
72f371c157 fix update on empty list of allowed address pairs
Story: 2008986
Task: 42636
Change-Id: I1a500673d870b706d9187ce9780fc72d3603bad9
2021-06-23 10:43:16 +00:00
Jakob Meng
ce421fe370 Only apply necessary changes to subnets
Previously, all subnet properties were updated if any value did not match.
But this behaviour caused errors like e.g. "Current gateway ip 192.168.0.1
already in use by port [ID]. Unable to update." even if that gateway was
not about to be changed.

Task: 40927
Story: 2008172
Change-Id: I049b0dade4c7ea3e1ef24777ae558f650caa136c
2021-06-23 10:40:57 +00:00
Ümit Seren
f20ec3a151 Add address scope module
Change-Id: If02e03f124a8677cba42aa7019947e8f00ea476f
2021-06-22 23:23:50 +02:00
Zuul
a89ec027b0 Merge "Switch Snapshot module to OpenStackModule" 2021-06-22 20:53:47 +00:00
Zuul
322f96c4db Merge "Switch Quota module to OpenStackModule" 2021-06-22 20:48:11 +00:00
Zuul
50b55560fc Merge "Switch user_info module to OpenStackModule" 2021-06-21 17:16:09 +00:00
Zuul
0c0013504e Merge "Switch ProjectAccess module to OpenStackModule" 2021-06-21 17:04:10 +00:00
Zuul
e651838f3e Merge "Switch role_assignment module to OpenStackModule" 2021-06-21 17:04:07 +00:00
Zuul
4553282545 Merge "Switch identity_role module to OpenStackModule" 2021-06-21 16:19:07 +00:00
Zuul
235628dace Merge "Switch hostaggregate module to OpenStackModule" 2021-06-21 15:20:38 +00:00
Zuul
4ee8621b37 Merge "Dns zone info module" 2021-06-17 17:09:58 +00:00
Zuul
d07039a7ec Merge "Switch lb_listener module to OpenStackModule" 2021-06-17 16:13:49 +00:00
Zuul
54728a83fa Merge "Switch lb_member module to OpenStackModule" 2021-06-17 16:13:46 +00:00
Zuul
8431232eeb Merge "Switch lb_pool module to OpenStackModule" 2021-06-17 15:58:57 +00:00
Zuul
b21b8b1084 Merge "Switch catalog_service module to OpenStackModule" 2021-06-17 15:06:35 +00:00
Zuul
e2ffed388e Merge "Switch group_assignment module to OpenStackModule" 2021-06-17 14:06:08 +00:00
Zuul
ff44a1d1e5 Merge "Switch KeystoneFederationProtocolInfo module to OpenStackModule" 2021-06-17 12:05:32 +00:00
Zuul
9f893824c5 Merge "Switch federation_mapping module to OpenStackModule" 2021-06-17 11:59:37 +00:00
Zuul
b9df964149 Merge "Switch federation_mapping_info module to OpenStackModule" 2021-06-17 11:20:33 +00:00
Zuul
0c8fe9002e Merge "Switch floating_ip module to OpenStackModule" 2021-06-17 11:20:20 +00:00
Zuul
e010db6273 Merge "Switch federation_idp_info module to OpenStackModule" 2021-06-17 11:20:09 +00:00
Zuul
88277a59af Merge "Switch port_info module to OpenStackModule" 2021-06-17 11:20:05 +00:00
Zuul
1a4d0128c7 Merge "Add check_mode attribute to OpenstackModule" 2021-06-17 11:20:02 +00:00
Polina Gubina
03a86669de Dns zone info module
Change-Id: I99f084bcb2fa3d4a203f439a55eef162ab82d43d
2021-06-17 10:52:05 +00:00
Artem Goncharov
fa7526a0ec Switch hostaggregate module to OpenStackModule
Change-Id: I86ebbcc52b6558f4110fc439ed13fb590dddb590
2021-06-17 13:41:22 +03:00
Artem Goncharov
7446ab23b8 Switch ProjectAccess module to OpenStackModule
Switch project_access module to the general OpenStackModule.

Change-Id: I359fcf8d815413b52b993b343c458c8c986bc247
2021-06-17 13:28:10 +03:00
Zuul
4171ee52b7 Merge "Switch endpoint module to OpenStackModule" 2021-06-16 20:46:52 +00:00
Zuul
60f1b8e2a5 Merge "Switch ServerGroup module to OpenStackModule" 2021-06-16 19:12:51 +00:00
Zuul
0bff3f286f Merge "Switch Stack module to OpenStackModule" 2021-06-16 19:12:48 +00:00
Zuul
5188e281b9 Merge "Switch flavor_info module to OpenStackModule" 2021-06-16 11:46:04 +00:00
Zuul
966b6597f3 Merge "Switch Recordset module to OpenStackModule" 2021-06-16 11:45:59 +00:00
Zuul
5636d502ce Merge "Switch identity_domain_info module to OpenStackModule" 2021-06-16 08:56:22 +00:00
Zuul
4db925b452 Merge "Switch auth module to OpenStackModule" 2021-06-16 08:20:43 +00:00
Zuul
3ce4455bc8 Merge "Switch coe_cluster_template module to OpenStackModule" 2021-06-16 08:20:22 +00:00
Zuul
309c3025ba Merge "Switch identity_user module to OpenStackModule" 2021-06-16 08:20:19 +00:00
Zuul
637d321a9a Merge "Switch flavor module to OpenStackModule" 2021-06-16 06:30:51 +00:00
Zuul
74c18f34f3 Merge "Switch identity_group_info module to OpenStackModule" 2021-06-15 18:25:46 +00:00
Zuul
00be40a462 Merge "Switch port module to OpenStackModule" 2021-06-15 18:25:43 +00:00
Zuul
1eb5d85b68 Merge "Switch coe_cluster module to OpenStackModule" 2021-06-15 17:25:29 +00:00
Zuul
d0a4d6e8cc Merge "Switch object module to OpenStackModule" 2021-06-15 14:22:05 +00:00
Zuul
29c03592cc Merge "Switch identity_group module to OpenStackModule" 2021-06-15 14:22:02 +00:00
Zuul
7888b51053 Merge "Switch federation_idp module to OpenStackModule" 2021-06-15 13:37:38 +00:00
Zuul
0e4dc21bab Merge "Switch identity_domain module to OpenStackModule" 2021-06-15 12:18:35 +00:00
Zuul
623b4afbd1 Merge "Floating ip info module" 2021-06-15 10:13:12 +00:00
Zuul
80e6597f96 Merge "Switch federation_protocol module to OpenStackModule" 2021-06-15 09:30:54 +00:00
Zuul
8f1512166e Merge "Switch ServerMetadata module to OpenStackModule" 2021-06-15 09:05:08 +00:00
Sagi Shnaidman
eabe945194 Add check_mode attribute to OpenstackModule
Change-Id: I4a779113f7bf293b5f059d662a1a562d4192e537
2021-06-14 14:04:47 +03:00
Artem Goncharov
06115c77b7 Switch Quota module to OpenStackModule
Switch quota module to the general OpenStackModule.

Change-Id: I28a61bf70b161358b76e3adb70b32896613e9aba
2021-06-14 13:48:29 +03:00
Zuul
4a1b092efb Merge "Fail if referenced source image for a new volume does not exist" 2021-06-07 15:46:16 +00:00
Zuul
da1fab6629 Merge "Fix host_aggregate to tolerate aggregate.hosts being None" 2021-06-07 15:45:40 +00:00
Zuul
0efb855c68 Merge "Update checks for validate_certs in openstack_cloud_from_module" 2021-06-07 15:45:33 +00:00
Georgina Shippey
329a5ef50d Update checks for validate_certs in openstack_cloud_from_module
Change I51105f11565c5ff33b04add36259c8703af11240 moved validate_certs default from None to False.
This causes checks to fail in openstack_cloud_from_module as validate_certs is never None anymore.

This patch changes reverts the default back to None, and reflects this in the documentation.

Task: 41776
Story: 2008600
Change-Id: Ic79510f863cf3a39c3f5c6d99f61d335f92f9388
2021-06-01 17:22:37 +01:00
Christian Rohmann
47708e0172 Fix host_aggregate to tolerate aggregate.hosts being None
Story: 2008925
Task: 42524

Change-Id: I0b2f8d0578b764ba3484d45c212f81ad728072e5
2021-06-01 12:45:44 +02:00
Polina Gubina
17d5c7de8e Floating ip info module
Change-Id: If932651c9d7a1819f805a49b020826682c5b8087
2021-05-27 12:03:04 +03:00
Christian Rohmann
82792ab5d5 Fail if referenced source image for a new volume does not exist
Story: 2008926
Task: 42525
Change-Id: I867d6869744528aae3034a7e0add2d0e922a97ff
2021-05-26 18:21:08 +02:00
Zuul
d4033b4cea Merge "Remove pip job" 2021-05-25 21:10:10 +00:00
Zuul
426b6e782a Merge "Make tripleo master job voting" 2021-05-25 21:10:06 +00:00
Zuul
75b2c60960 Merge "Fix inventory plugin on Ansible 2.11" 2021-05-25 19:59:49 +00:00
Sagi Shnaidman
c9527889a0 Remove pip job
Pip installation is not recommended for collections anyway, so
let's drop this check to make CI more tiny.
Change-Id: I3d02f0c8101a5c46404563b64e8bea78fe422a59
2021-05-25 20:48:01 +03:00
per-lind
42a5cb5601 Fix inventory plugin on Ansible 2.11
The bug here is that the openstack inventory plugin will no longer work with Ansible 2.11. You can test an inventory file (named openstack.yml) with either of these syntaxes:

plugin: openstack
plugin: openstack.cloud.openstack
The first option errors due to custom validation present in openstack's own inventory plugin:

[WARNING]: * Failed to parse /home/alancoding/repos/awx/testing/openstack/openstack.yml with auto plugin: plugin
config file, but not for us: openstack

Because this was written back before FQCNs (fully-qualified collection names) were a thing. Before it migrated to a collection, "openstack" was the expectation, but then self.NAME for the inventory plugin changed to "openstack.cloud.openstack", meaning that "openstack" by itself would no longer work. That made sense until Ansible core introduced routing where it would recognize "openstack" and route it to "openstack.cloud.openstack" for purposes of the "auto" inventory plugin routing. See the routing entry at:

2cbfd1e350/lib/ansible/config/ansible_builtin_runtime.yml (L9548)

The second option errors with:

[WARNING]: * Failed to parse /home/alancoding/repos/awx/testing/openstack_fqcn/openstack.yml with auto plugin:
Invalid value "openstack.cloud.openstack" for configuration option "plugin_type: inventory plugin:
ansible_collections.openstack.cloud.plugins.inventory.openstack setting: plugin ", valid values are: ['openstack']

This is due to Ansible core enforcing stricter validation of options. Merged in this PR ansible/ansible#73162

That broke many inventory plugins because the practice before the migration to collections was to list the name in the choices for the "plugin" option. This has been fixed in other collections.

Because neither of these options work, the inventory plugin is not usable in recent Ansible versions.

Suggested patch here:

https://github.com/AlanCoding/ansible-collections-openstack/compare/fqcn_name?expand=1

This allows the user to use either syntax, because there's an argument for the validity of both.

credit to https://github.com/AlanCoding

Change-Id: Ie1211796929d0bc12c7a48764bd8efc7defdd2d7
2021-05-25 19:42:04 +02:00
Sagi Shnaidman
aa387aab3a Make tripleo master job voting
And train as non-voting - mostly for informational purpose.
Change-Id: Ib0d186865591922614d8d7f786da520cd26c3976
2021-05-25 13:45:31 +03:00
Artem Goncharov
af79857bfb Switch Stack module to OpenStackModule
Switch stack module to the general OpenStackModule.

Change-Id: I98006adcb16c57d5d00990ef6cb76298ff9af9f9
2021-05-25 10:37:19 +02:00
Artem Goncharov
a51d922bee Switch identity_domain module to OpenStackModule
Change-Id: I8d7250cbc5396af25b9720c6f5ccb3925a398ac7
2021-05-21 12:15:29 +00:00
Artem Goncharov
0cbaeb6fbb Switch identity_role module to OpenStackModule
Change-Id: I57c2c558bd01c984453fb11048e8a888ec4d9eea
2021-05-21 12:14:49 +00:00
Artem Goncharov
532857c0b2 Switch federation_idp module to OpenStackModule
Change-Id: I9de209373991c8d999b0514549ad5808981441f1
2021-05-21 14:14:16 +02:00
Artem Goncharov
acd9bc993b Switch group_assignment module to OpenStackModule
Change-Id: I443ed6785500a0e4aafe9c976a11c022e8f23db0
2021-05-21 12:13:15 +00:00
Artem Goncharov
0904ddd2fe Switch federation_protocol module to OpenStackModule
Change-Id: I28a69171df0d4b2df768458f0b5cff72c3937f59
2021-05-21 14:12:08 +02:00
Artem Goncharov
2120814356 Switch KeystoneFederationProtocolInfo module to OpenStackModule
Switch keystone federation_protocol_info module to the general OpenStackModule.

Change-Id: Ia42c602ad7ea01dcb27d77370cc2617ec51aaaf3
2021-05-21 14:07:25 +02:00
Artem Goncharov
ad70a20af5 Switch identity_group module to OpenStackModule
Change-Id: I880e425a5b334abfe7b9498d70229427292c2296
2021-05-21 14:05:25 +02:00
Artem Goncharov
a32cff23c7 Switch Recordset module to OpenStackModule
Switch dns.recordset module to the general OpenStackModule.

Change-Id: I858880834acce6112eadd7e78e339f39b7c455da
2021-05-21 14:04:34 +02:00
Artem Goncharov
f29a8407af Switch port module to OpenStackModule
Switch port module to the general OpenStackModule.

Change-Id: I66fcc0bc126563a6652253151e098fab62f9c68f
2021-05-21 14:02:49 +02:00
Artem Goncharov
555178ecc6 Switch user_info module to OpenStackModule
Drop deprecated block to keep simplicity.

Change-Id: I9e64913cd157f2985ef720ddecfbeb9ad996fa2c
2021-05-21 14:01:46 +02:00
Artem Goncharov
f93172677c Switch ServerGroup module to OpenStackModule
Switch ServerGroup module to the general OpenStackModule.

Change-Id: Ib80483fbc6c3d93003b690997014d8d08315296d
2021-05-21 13:57:38 +02:00
Artem Goncharov
2a461cadd9 Switch ServerMetadata module to OpenStackModule
Switch ServerMetadata module to the general OpenStackModule.

Change-Id: If3f3086a964c47407ec82dda662d2c1524283fb6
2021-05-21 13:56:43 +02:00
Artem Goncharov
e3c61aeefd Switch Snapshot module to OpenStackModule
Switch snapshot module to the general OpenStackModule.

Change-Id: I0f4281324e5276122f51722c7b8e79ee185fd476
2021-05-21 13:47:50 +02:00
Artem Goncharov
3e4413ec31 Switch auth module to OpenStackModule
Change-Id: I6e81a6a8171f62651dce0a4b3cc794b045a5a453
2021-05-21 07:47:42 +00:00
Artem Goncharov
37afe5f2e6 Switch catalog_service module to OpenStackModule
Change-Id: I77332c1af2311a472702e90f72ea9b21d385df3c
2021-05-21 07:46:26 +00:00
Artem Goncharov
7e190d12b4 Switch coe_cluster module to OpenStackModule
Change-Id: Idb316136840001e406f76c08d99654d32e14d168
2021-05-21 07:46:15 +00:00
Artem Goncharov
3d9f9a0f87 Switch coe_cluster_template module to OpenStackModule
Change-Id: I486d98d3f4ec480254a0dcb82a5cb48a9ecc8d3f
2021-05-21 07:44:43 +00:00
Artem Goncharov
57e1087177 Switch flavor module to OpenStackModule
Change-Id: I49302c7ca72b85c74c00e436bc4d695115471f70
2021-05-21 07:44:28 +00:00
Artem Goncharov
6cb6e70645 Switch flavor_info module to OpenStackModule
Change-Id: I96f876bdf1eab0e451ee6d251b62f6aaa52ffc6f
2021-05-21 07:43:56 +00:00
Artem Goncharov
662ac61640 Switch endpoint module to OpenStackModule
Change-Id: Ibd208374297342764c79ffe3968e360cc3ae004d
2021-05-21 07:43:44 +00:00
Artem Goncharov
891fb6df53 Switch federation_idp_info module to OpenStackModule
Change-Id: I7d37e83da3bccd13ed4c7bd5f0dce5bfe9e205cb
2021-05-21 07:42:41 +00:00
Artem Goncharov
0c6752215b Switch federation_mapping module to OpenStackModule
Change-Id: If50b83aaae9eb684624a5fe34415c3b67d10b65f
2021-05-21 07:42:30 +00:00
Artem Goncharov
a6f1177584 Switch federation_mapping_info module to OpenStackModule
Change-Id: Ia0abcaea7694396a6c7b7f8fd01a3b29f27c7729
2021-05-21 07:40:56 +00:00
Artem Goncharov
2dc2806c57 Switch floating_ip module to OpenStackModule
Change-Id: I0d406b527a2389d5299ab4e5f5c557737fe2ad99
2021-05-21 07:40:05 +00:00
Artem Goncharov
59df183660 Switch identity_domain_info module to OpenStackModule
Change-Id: If866ef04ae822b09b162773262e49ec8f33f5e92
2021-05-21 07:38:50 +00:00
Artem Goncharov
2c287d754c Switch identity_group_info module to OpenStackModule
Change-Id: I6e425cad55e7c1c9df44c4f2688c023e5011d2e9
2021-05-21 07:38:13 +00:00
Artem Goncharov
28d32b53e8 Switch identity_user module to OpenStackModule
Change-Id: I61a43ac31f23758204d11fe72754a50993dcc294
2021-05-21 07:36:31 +00:00
Artem Goncharov
2cb67620aa Switch lb_listener module to OpenStackModule
Switch loadbalancer listener module to the general OpenStackModule.

Change-Id: Ifa6372bc5ee18e3ec47edaf630a3149609e1db0b
2021-05-21 07:33:15 +00:00
Artem Goncharov
5217b6bdc4 Switch lb_member module to OpenStackModule
Switch loadbalancer member module to the general OpenStackModule.

Change-Id: I92d09c27205f8fa01df9ddab96448c433c42ef79
2021-05-21 07:32:54 +00:00
Artem Goncharov
96ea60a883 Switch lb_pool module to OpenStackModule
Switch loadbalancer_pool module to the general OpenStackModule.

Change-Id: I75dfd34dd65f73ae8d7584729aa0c14fd14ee366
2021-05-21 07:32:00 +00:00
Artem Goncharov
0eef3cb978 Switch object module to OpenStackModule
Switch object module to the general OpenStackModule.

Change-Id: Ic90d796fe4bbec118c148ffd468458bd98990422
2021-05-21 07:30:55 +00:00
Artem Goncharov
e695000daa Switch port_info module to OpenStackModule
Switch networking port_info module to OpenStackModule. Drop part of
deprecated functionality to keep it simple.

Change-Id: I1d310e369ba2fde76478d9751bd8151fe20e2ba7
2021-05-21 07:29:51 +00:00
Artem Goncharov
0258878400 Switch role_assignment module to OpenStackModule
Switch role_assignment module to the general OpenStackModule.

Change-Id: I67df08cced62fb729cb1d4fbb43ac5977fca61de
2021-05-21 09:25:58 +02:00
Paul Belanger
ea7ba854d5 Add bindep.txt for ansible-builder
This allows users to properly build openstacksdk when using
ansible-builder.

Signed-off-by: Paul Belanger <pabelanger@redhat.com>
Change-Id: I6cef73dedeac75a072309f7f2b6682906102a20d
2021-05-19 14:04:33 -04:00
Dmitriy Rabotyagov
6b3bf3bba0 Add support to setting image tags
Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/783849
Change-Id: Idee1f3c16cb07db39395b434d406628a9c5b152d
2021-05-18 16:30:01 +00:00
Zuul
daaa6bf561 Merge "Switch project and project_info module to OpenStackModule" 2021-05-12 21:31:19 +00:00
Sagi Shnaidman
2027eb1f46 Add non-voting train TripleO jobs
Because we use modules starting from train, let's check we don't
break anything in train branch for TripleO.
Use non-voting more for informational purpose, since old branches
jobs can be unstable.

Change-Id: I0026b3bdd785a32b7d701ed6ac146853c0cdd162
2021-05-11 12:16:37 +03:00
Artem Goncharov
944f9ca498 Switch project and project_info module to OpenStackModule
Continuing our journey on generalization of the modules switch the
project modules.

Change-Id: I29c689212c07e1e582cffcdd47496975ac0d327c
2021-05-11 09:11:50 +00:00
Sagi Shnaidman
ca6824bffe Configure only one tripleo job
Because we run only one job, no need to set content provider.
Change-Id: I85ad4281906a6d4aec4dceb4e6fc985e8c60f1a8
2021-05-11 01:04:29 +03:00
Sagi Shnaidman
a10be98638 Add TripleO job on collections used in TripleO
Redefine irrelevant files for jobs.

Change-Id: I83616ca370a449dcead99aed03fe4e54358e9e5a
2021-05-06 17:53:26 +03:00
Zuul
6787924f13 Merge "Fix the idempotent of compute_flavor module" 2021-05-04 09:25:49 +00:00
Zuul
d1d6b4d2dd Merge "setup.cfg: Replace dashes with underscores" 2021-04-29 13:46:33 +00:00
Zuul
06e76a1687 Merge "Remove CI jobs for rocky and stein branches from check" 2021-04-29 13:26:03 +00:00
Zuul
464d7767bb Merge "Migrating image module from AnsibleModule to OpenStackModule" 2021-04-29 12:09:05 +00:00
Sagi Shnaidman
718f3a6b3a Remove CI jobs for rocky and stein branches from check
We don't want to support full compatibility with rocky and stein
branches. Move CI jobs for them to experimental pipeline.
Change-Id: I738831bada5c4779869024c5660d9a7607a603c5
2021-04-29 12:57:12 +03:00
yangyawei
3320c662c8 setup.cfg: Replace dashes with underscores
Setuptools v54.1.0 introduces a warning that the use of dash-separated
options in 'setup.cfg' will not be supported in a future version [1].
Get ahead of the issue by replacing the dashes with underscores. Without
this, we see 'UserWarning' messages like the following on new enough
versions of setuptools:

  UserWarning: Usage of dash-separated 'description-file' will not be
  supported in future versions. Please use the underscore name
  'description_file' instead

[1] https://github.com/pypa/setuptools/commit/a2e9ae4cb

Change-Id: I01ed896fb685dc6e8873b26942fa1c85c897a25f
2021-04-29 17:38:54 +08:00
wuchunyang
77acba047b Fix the idempotent of compute_flavor module
if flavor's swap is not set (or 0),nova returns empty string [0]
rather than 0 when query flavor spec. this behavior will break
the idempotent of the ansible deployment. this ps changes the empty
string to 0 before compare them.
[0]: https://docs.openstack.org/api-ref/compute/?expanded=list-flavors-detail,list-flavors-with-details-detail#list-flavors-with-details

Change-Id: I041cbcc0432051d795e2c3f0e7ac7378e1970008
2021-04-27 18:12:29 +08:00
anbanerj
890401b605 Migrating image module from AnsibleModule to OpenStackModule
1. Added ImageModule class
2. Added deprecated_names
3. Updated types in param

Change-Id: I58beb5cab26106769d18e25cd3d23df68145e363
2021-04-22 10:32:46 +02:00
Sagi Shnaidman
8da4e852f0 Add wallaby job to CI
Add voting for train.
Change-Id: I5ecdc746056edf59d647b73c7fa7d316c092b38a
2021-04-20 04:05:12 +03:00
Sagi Shnaidman
f3e9c78c18 Bump to dev version
Change-Id: I8a10b36eef9a98fe1a4b43a7c6ce8986950154ad
2021-04-09 00:23:25 +03:00
Sagi Shnaidman
47382d52ed Release 1.4.0 version
Change-Id: I64652090e65de8450ca907a57e23e09e562c17da
2021-04-08 20:03:26 +03:00
Polina Gubina
0e2058988e Container module for management Swift containers
Change-Id: I13161b360addac2d2c1c507145b5be653d6dec17
2021-04-08 18:46:53 +03:00
Zuul
ab1a2a19d7 Merge "Prepare for Ansible 2.11 tests" 2021-04-08 10:49:56 +00:00
Sagi Shnaidman
17a1d72b1c Prepare for Ansible 2.11 tests
Change-Id: I3914db6e5c4f279f2aeae051fd6a1a4254f554c9
2021-04-08 00:19:24 +03:00
Zuul
58ec1030a4 Merge "Add execution environment metadata" 2021-04-07 19:26:45 +00:00
AlanCoding
da01746e53 Add execution environment metadata
Change-Id: Ic68fe6fd43aae12f944ad17f747f6bf37bf4d324
2021-04-07 14:41:19 +00:00
Sagi Shnaidman
c329f65b41 Fix issues with newest ansible-test 2.11
Change-Id: Ifdf253ca01b0e19b55867d8ead03eaceb5b2d73a
2021-04-07 14:47:00 +03:00
Sagi Shnaidman
18b03e1971 Fix CI for latest ansible-test with no_log
Add no_log for requires arguments, hide admin_password for server
actions.
Disable voting for stein, rocky, queens jobs as they are broken
atm in devstack part.
Change-Id: Ib408749bedb583b065f82237a223388a7d919640
2021-04-05 22:24:31 +03:00
Vladimir Ermakov
16a81fc221 security_group_rule: add support ipv6-icmp
Adds ipv6-icmp protocol to _ports_match check to be able to make that
rules for IPv6.

Story: 2008687
Task: 41989
Change-Id: Ib6313788132bb601d7d53ac709b7c822ee533a8b
2021-03-04 13:06:57 +03:00
Maxim Babushkin
50deae71f4 Add binding profile to port module
Neutron port is able to configure binding profile during creation.
Add support of the binding profile to module

Change-Id: I7a36fb05065cbd559cd7a00842070f97e7afbc13
2021-03-01 14:16:51 +02:00
Zuul
b1e5ca91cf Merge "add option to exclude legacy groups" 2021-02-22 13:31:48 +00:00
Shnaidman Sagi (Sergey)
185d0a5eca Revert "Mark 2.9 ansible-test sanity as non-voting"
This reverts commit 1d8605fbf7.

Reason for revert: fixed in Ansible: https://github.com/ansible/ansible/pull/73618

Change-Id: I6eca78cca86c57e166f45c2d5cbd79e8b5891857
2021-02-21 11:02:23 +00:00
Sagi Shnaidman
2b8aa2cb08 Add Octavia job for testing Load Balancer
Add basic test for LB.
Configure Octavia job which will run on LB changes only.
Change-Id: Ic76bb766f133c91a41893978ee864025dd659ab4
2021-02-20 17:57:20 +02:00
willtome
3c8fbc6b27 add option to exclude legacy groups
Change-Id: I73835b111b1bdfc0d8dd6409d516ad1cb84c658a
2021-02-18 12:03:09 +00:00
Sagi Shnaidman
d212be018b Bump dev version in galaxy.yml
Change-Id: Iac46e2290d5b10e39f4d480d69ff1284f535c2e9
2021-02-16 21:41:48 +02:00
Sagi Shnaidman
573e219e30 Release version 1.3.0 of Openstack Collection
Change-Id: I024e0ca9259a21956fea7f3cbd3e2c954b4dea39
2021-02-16 19:33:59 +02:00
Zuul
5c3750df2c Merge "ironic: stop putting meaningless values to properties" 2021-02-16 11:47:39 +00:00
Zuul
94e0e10e49 Merge "Allow description field to be set with os_server" 2021-02-16 10:43:53 +00:00
Zuul
db9a8d5a18 Merge "Guidelines: Fix links and formatting" 2021-02-16 09:41:03 +00:00
Zuul
7e20a8fc97 Merge "Migrating image_info module from AnsibleModule to OpenStackModule" 2021-02-16 00:43:51 +00:00
Zuul
c7969aa052 Merge "ironic: deprecate sub-options of driver_info" 2021-02-15 23:20:05 +00:00
Zuul
179a500903 Merge "Fix some typos in readme" 2021-02-15 22:23:25 +00:00
Sebastian Haderecker
ab3e136867 Guidelines: Fix links and formatting
Change-Id: I658ff486414100944b2f39d8068495cc7765fbe5
2021-02-15 20:49:37 +00:00
anbanerj
8d2391d873 Migrating image_info module from AnsibleModule to OpenStackModule
1. Added ImageInfoModule class
2. Added basic test to see if image_info reflects created image and deletion of image
3. Added deprecated_names
4. Added 'type'/'required' in argument_spec
5. Removed debug statements from test as we are checking already with image_info module

Change-Id: I6ab5fd5384392f9de0af01b1937a75de4f16d28d
2021-02-15 20:49:19 +00:00
Manuel Rodriguez
e1d62ff73a Allow description field to be set with os_server
Adds server description field support to os_server
when compute API version >= 2.19
https://review.opendev.org/c/openstack/openstacksdk/+/775513

Story: 2008395
Depends-On: dab55a7
Change-Id: I848c5e489ef9fe071362c79b2c14fb45724cb4f5
2021-02-15 14:30:59 -05:00
Sebastian Haderecker
c3261d9813 Fix some typos in readme
Change-Id: I6a6c93c3764d075a3dca5719b7fe561867064c07
2021-02-15 19:50:25 +01:00
Sagi Shnaidman
1d8605fbf7 Mark 2.9 ansible-test sanity as non-voting
Current way of running ansible-test with venv is failing because
of cryptography package issue[1]. Mark it non-voting until the
issue is resolved.

[1] https://github.com/pyca/cryptography/issues/5771

Change-Id: Ife1468c4c7140ed3bdaf1dad06a09c71b8e1dbd4
2021-02-15 15:53:56 +00:00
Dmitry Tantsur
0843990dee ironic: stop putting meaningless values to properties
None of the properties are required nowadays, putting made-up values
there brings more harm than good.

Change-Id: I35bda0ac2dc9c32acb94aaa4d28572af2cac85fa
2021-02-12 14:38:27 +01:00
Zuul
6f8b39bb16 Merge "Add modules for roles information" 2021-02-06 03:28:01 +00:00
Sagi Shnaidman
c39c8f9d74 Add modules for roles information
Add module that retrieves list of roles for a Openstack cloud.
Change-Id: Iabadd94f990c49ba078aa02e2d801c40985f85b8
2021-02-04 04:30:07 +02:00
Artem Goncharov
e4c7bd3df8 Add security_group_info module
Let's add a new missing module for getting info about security groups.
Add tests.

Change-Id: Ib032c8d14444cea1fcbfd98d252cc56b9f5f383e
2021-02-04 02:22:31 +02:00
Zuul
8a4974025f Merge "Add security_group_rule_info module" 2021-01-28 18:19:52 +00:00
tischrei
51a9731cef Add security_group_rule_info module
Let's add a new missing module for getting info about security group rules.

Change-Id: Iba2fe66c4bd19ab66f8e35c091ed4c0ea423efd1
2021-01-28 10:43:26 +00:00
Zuul
051b270d36 Merge "Fixed check for None in os_port" 2021-01-27 14:04:11 +00:00
Zuul
ca234d7e42 Merge "New keypair_info module" 2021-01-27 13:41:47 +00:00
Zuul
5967e1ad0a Merge "Fix setting custom property on os_project" 2021-01-27 12:26:10 +00:00
Irina Pereyaslavskaya
bca980c115 New keypair_info module
Change-Id: Idc3c50e9857b9bc87767cc93096fe7a8b24b298e
2021-01-25 13:57:17 +03:00
Zuul
a55b817968 Merge "Add stack_info module" 2021-01-22 11:30:06 +00:00
Lucas Galton
b7c22515b7 Fix setting custom property on os_project
When a new property is created on a project, verifies if the
key is in the dict before checking its value for changes.

Story: 2008530
Task: 41613
Change-Id: I5db452e0719b45932dda068e32f4876c098fea77
Signed-off-by: Lucas Galton <lucas@galton.fr>
2021-01-20 12:26:15 +01:00
Artem Goncharov
70c773fe6d Add stack_info module
We lost stack_info module during transition from github. Implement it
using newer interface and add tests. Change depends on SDK change adding
missing query filters.

Depends-On: https://review.opendev.org/c/openstack/openstacksdk/+/769484
Change-Id: Ie7e6d04ea298ba068f547a53643806b6bc84f873
2021-01-18 18:19:48 +00:00
Sagi Shnaidman
a3f809eb99 Remove protocols choice in security rules
Security group rule can support a lot of protocols now and their
integer representations in 0-255 range. Let's not limit for this
option anymore.

Change-Id: I0432e5d5d6d5f5ce935cf59a00f35153649c22dd
2021-01-15 20:58:54 +00:00
Zuul
049a2de725 Merge "Fix volume_info result for SDK < 0.19" 2021-01-15 16:46:15 +00:00
Jakob Meng
69947cd9fd Fix volume_info result for SDK < 0.19
With openstacksdk prior to version 0.19, module volume_info fails with:
  TypeError: Value of unknown type: <class
  'openstack.block_storage.v2.volume.Volume'> ...

The call to 'self.conn.block_storage.volumes()' returns an instance
of class 'openstack.block_storage.v2.volume.Volume'. This class
inherits from 'openstack.resource.Resource', which is a 'dict'
subclass since 0.19. For older sdk versions prior to 0.19 it was
not, hence we have to use the 'to_dict' function to convert each
'Volume' to 'dict' explicitly.

Ref.: 2f97394847

Task: 41571
Story: 2008136
Change-Id: I5b7adc399f19da08f02202af64a226c92bb9bf41
2021-01-13 14:17:58 +01:00
Zuul
c68b33f79f Merge "Added shelve and unshelve as new server actions" 2021-01-12 13:55:54 +00:00
Zuul
62f0da75bb Merge "Update recordset docu" 2021-01-12 12:13:29 +00:00
Jakob Meng
6e2bde4060 Added shelve and unshelve as new server actions
Task: 40737
Story: 2008056
Change-Id: I127ee59be74e9ebcf7f36b3fabdd6e41a5f7a3bb
2021-01-12 10:15:38 +01:00
Jakob Meng
e4a223d160 Fixed check for None in os_port
Fix bug introduced in commit cac93cb
cac93cbd1f

Task: 40928
Story: 2008173
Change-Id: I990a68d4e24c3c3953010e4053c73454e56f0d64
2021-01-11 13:32:50 +01:00
Dmitry Tantsur
6d4d8d4b0c ironic: deprecate sub-options of driver_info
This structure does not reflect the actual ironic API and is mostly
meaningless nowadays. Just let people populate driver_info directly.

Change-Id: I04d168dc86e8c0115b7183b14499fe1812af7343
2021-01-09 16:31:47 +01:00
Zuul
a4e6d1b67c Merge "Add support for new features" 2021-01-09 13:02:13 +00:00
Sebastian Haderecker
3aaf7ff03a Update recordset docu
Updating a recordset always requires recordset_type and records to be there.
Fixes: https://storyboard.openstack.org/#!/story/2008465

Signed-off-by: Sebastian Haderecker <sebastian.haderecker@gmail.com>
Change-Id: Ia50085337813fb3bd3e72cb70fa62072cce5b791
2021-01-08 23:49:51 +01:00
Tosin Farai
f6a5433503 Add support for new features
Story: 2008432
Task: 41385
Change-Id: I8449075b9a0b39bdc9de8fa94cf238e5e8554875
2021-01-05 01:00:08 +01:00
Sagi Shnaidman
92c10638a9 Add dev tag for collection install
Bump a tag ater release for collection install from git.
Change-Id: Ic9905360dcf3bc5ffc11394b56e3df73fa3d361a
2021-01-03 23:51:57 +02:00
Sagi Shnaidman
e3317db56d Release 1.2.1 version of Openstack Ansible modules
Change-Id: Ia775e7df0ceb8ce0eef3f6d11b957d223729de57
2021-01-03 17:04:29 +02:00
Sagi Shnaidman
870f82d97b Decrease MTU in networks tests
Not all environments support MTU 1350, let's decrease it to 1250.
Change-Id: I6c03c0a1492394eb9fb5de8b0815b459437895cb
2020-12-31 14:07:56 +02:00
Polina Gubina
87c305907e Enable update for recordset and add tests for dns and recordset module
Update for recordset wasn't working properly and there are no tests
for dns and recordset modules, minor fix in dns_zone

Change-Id: I7f78f6038dfb858e795b1954eae11cff47f697ad
2020-12-29 11:34:27 +00:00
Zuul
c532560d1b Merge "Migrating subnet module from AnsibleModule to OpenStackModule" 2020-12-21 14:21:11 +00:00
anbanerj
ea1f1df805 Migrating subnet module from AnsibleModule to OpenStackModule
1. Created class SubnetModule with run method
2. Changed argument_spec from openstack_full_argument_spec to dict
3. Moved "netid != subnet['network_id']:" check inside "if network:" loop
4. Moved the methods using "module" inside SubnetModule class

Change-Id: I8f19359e70f8565ebfb096d30ab75e727f748be6
2020-12-18 13:49:52 +01:00
Zuul
40a32c1e8b Merge "Аdd designate to install with devstack" 2020-12-17 19:23:00 +00:00
Zuul
5881f5423a Merge "Separate volume tests from servers tests" 2020-12-17 15:06:07 +00:00
Sagi Shnaidman
51eba6de02 Аdd designate to install with devstack
Allow DNS tests
Change-Id: I215ce23bbfe68437523e5ee608508dfcc028731a
2020-12-17 06:22:56 +00:00
Zuul
93faf4f1c3 Merge "Add network tests for versioned args" 2020-12-16 17:11:08 +00:00
Zuul
c03284abec Merge "Migrating network from AnsibleModule to OpenStackModule" 2020-12-16 16:59:29 +00:00
frenzy_friday
49c95804ba Add network tests for versioned args
1. Updated Create network tasks with mtu and port security enabled params which are supported for sdk version >= 0.18
2. Added task to assert that network creation with new params fail if sdk version is lower than 0.18

Change-Id: I7d65d8553e820344f76cf1092e0a373c8100b7c8
2020-12-16 08:52:05 +00:00
Sagi Shnaidman
c8a5be6b30 Separate volume tests from servers tests
Tag volume tests in servers as "object" so we can skip them when
volumes are not available.
Change-Id: I23488a64faa3a09493a84bf8eae239197e991d7e
2020-12-16 05:27:19 +02:00
Sagi Shnaidman
8255ec4c80 Run images tests
Change-Id: I972bd4d47b92e3a92876c62ab8fd3e8f67be4cd4
2020-12-16 05:22:40 +02:00
Zuul
36ce09a781 Merge "Migrating security_group from AnsibleModule to OpenStackModule" 2020-12-15 16:39:04 +00:00
frenzy_friday
0ac75add62 Migrating network from AnsibleModule to OpenStackModule
1. Created class NetworkModule with run method
2. Changed argument_spec from openstack_full_argument_spec to dict
3. Removed checking min_version for individual parameters and instead used check_versioned method for all together.
4. Since create_network method uses "mtu_size" as a parameter and not "mtu", changed the key "mtu" to "mtu_size" in argument spec and added alias as "mtu" to still support mtu keyword in playbook.
5. Changed "mtu" to "mtu_size" to match in the doc

Change-Id: Ic4ddedb43044434df0a18f8aacacd21149e6f0b0
2020-12-15 15:39:22 +01:00
Zuul
d004e0af05 Merge "Migrating dns_zone from AnsibleModule to OpenStackModule" 2020-12-15 13:13:23 +00:00
Zuul
b040392238 Merge "Migrating routers from AnsibleModule to OpenStackModule" 2020-12-15 12:47:39 +00:00
Kristian Kucerak
19f24568a2 Migrating dns_zone from AnsibleModule to OpenStackModule
Change-Id: Ib8e5a6afe0ce6a7a095ac489ceed8879926ba7f5
2020-12-15 06:56:00 +00:00
frenzy_friday
9783fbb972 Migrating server_volume from AnsibleModule to OpenStackModule
Change-Id: I55cc89b9d043952c9bec6dccf3857a8c7713a7bb
2020-12-15 06:50:34 +00:00
frenzy_friday
b87e474192 Migrating routers from AnsibleModule to OpenStackModule
Change-Id: I6a444c33f2260b79a4f8f75ed5fe73d64fc85c06
2020-12-15 06:50:19 +00:00
frenzy_friday
c1b8786160 Migrating security_group from AnsibleModule to OpenStackModule
Change-Id: I2d861826d0e85f11f4a9d2eefc5a8e63fd1eb72e
2020-12-15 06:49:59 +00:00
Sagi Shnaidman
981d268039 Fix branchful jobs for collections
Stable branch jobs were using master for deployment

Story: #2008445
Task: #41412

Story: #2008444
Task: #41411

Mark train non-voting, see https://review.opendev.org/766622
Change-Id: I8132ec7cfe3468daaa363efb76c5d0b81bdeab30
2020-12-14 17:44:41 +02:00
Zuul
af27a79312 Merge "Migrating volume from AnsibleModule to OpenStackModule" 2020-12-08 16:05:03 +00:00
Sagi Shnaidman
e504d807de Fix docs-args mismatch in modules
Change-Id: I51105f11565c5ff33b04add36259c8703af11240
2020-12-08 12:06:42 +02:00
Vladimir Hasko
faada98ed9 Migrating volume from AnsibleModule to OpenStackModule
A bit restructed structure so decision logic whether volume will be created or updated is now in run method

Change-Id: I83e03787b3cea65f07dc83764743702d59e8656d
2020-12-07 14:22:22 +00:00
Sagi Shnaidman
c914c42799 Improve "server" module with OpenstackModule class
Move all functions that requires OpenstackModule methods to
OpenstackModule class.

Change-Id: I530413cdb6df782556006ff4de78242679f3f5c5
2020-12-03 15:32:14 +02:00
anbanerj
d5c403cded Migrating subnets_info from AnsibleModule to OpenStackModule
Updated module, added "deprecated_names", Removed "ansible-deprecated-no-collection-name" for subnets_info for ansible 2.10, 2.11

Change-Id: I5590976964543188518200f2b31a1603eb30f39b
2020-12-02 13:27:10 +01:00
frenzy_friday
d36ac1f125 Migrating networks_info from AnsibleModule to OpenStackModule
Migrated networks_info module to OpenStackModule and updated playbook to test the module in CI jobs. Added deprecated_names tyo module.
Removed "ansible-deprecated-no-collection-name" exception for networks_info for ansible 2.10 and 2.11. Reverted 'False' to 'false' and updated filters (case sensitive) to check for 'False' instead

Change-Id: I85e19f0db8b4ee549137249477d0b7f5d82e9865
2020-12-01 22:35:03 +00:00
Zuul
15675ce23f Merge "Refactor loadbalancer module" 2020-11-30 20:20:54 +00:00
Zuul
8180fe8af8 Merge "Refactor TCP/UDP port check." 2020-11-30 20:10:59 +00:00
Zuul
88f03fa1df Merge "Add tests for volume_info module" 2020-11-27 00:01:18 +00:00
Zuul
565f7fd369 Merge "Fix subnets update and idempotency" 2020-11-26 19:56:28 +00:00
Sagi Shnaidman
47a0d625dc Add tests for volume_info module
Add tests for volume_info

Change-Id: I8f30eed2a9d5183d0d38a89a7d39e34f7e7c2212
2020-11-26 21:49:32 +02:00
Zuul
b09d8248f7 Merge "Fix volume_info arguments for SDK 0.19" 2020-11-26 18:06:32 +00:00
siavashsardari
bce3eea5c0 Refactor TCP/UDP port check.
Task: 41314
Story: 2008390
Change-Id: Ib479dbef68cede6189d25e75388d8cb1fc61f95f
2020-11-26 17:49:14 +02:00
Sagi Shnaidman
134a8e9d23 Fix subnets update and idempotency
Fix subnet idempotency for allocation pools, see the linked story.
Return updated subnet information.
Remove adding allocation pools that were introduced in
Ib8becf5e958f1bc8e5c9fd76f1722536bf1c9f1a
in order to add allocation pools, either add new variable or
recreate the subnet.

Task: 41307
Story: 2008384

Change-Id: Ibe808227de159c6975dc94ef8ad0ab03a9345e17
2020-11-26 18:29:40 +03:30
Sagi Shnaidman
9ed9b1d399 Fix Ansible devel jobs
Change-Id: I2ce5b1f8cbb673d70a0a2250862009dfeb399d0e
2020-11-24 22:40:09 +02:00
Sagi Shnaidman
80abd782da Fix volume_info arguments for SDK 0.19
all_projects is not supported before openstacksdk 0.19,
use min_ver for using it.

Task: 40865
Story: 2008136
Change-Id: I0f02a47c11122c5b07ca650a830044bca56c3610
2020-11-24 19:48:19 +02:00
Sagi Shnaidman
ee9a5c564e Add victoria stable branch job
Change-Id: I183c9915be6442018dea93b32a4a93bb6df7acae
2020-11-24 13:19:53 +02:00
Zuul
393b484e5a Merge "Move CI jobs to base on Ansible 2.10 release" 2020-11-24 11:06:22 +00:00
Sagi Shnaidman
6117f7062e Move CI jobs to base on Ansible 2.10 release
Change-Id: Ib1884a1a7b69044cf7d0ac9469c677593339eb5c
2020-11-23 21:50:57 +02:00
anbanerj
f89eea10b4 Added deprecated_names for router_info module
Change-Id: I15bc654f1567ebfa4319523be4a9a8f4124898aa
2020-11-23 18:59:20 +01:00
anbanerj
e1178fde34 Migrating routers_info from AnsibleModule to OpenStackModule
Change-Id: I0b87c5c3336849bd2e62da5dee04614f74714dbf
2020-11-19 12:27:27 +02:00
Dmitriy Rabotyagov
8b35c64fda Do not fail when endpoint state is absent
In case endpoint state is absent we shouldn't fail in case service does
not exist, since it means that we're ok, and endpoint is not present.

This might be pretty useful, when user tries to create and delete service
and endpoint with the same code ie [1]

[1] https://opendev.org/openstack/openstack-ansible-tests/src/branch/master/sync/tasks/service_setup.yml

Change-Id: If7ecd7b2e28c81ffe18539731edd4efa599c42ec
Closes-Bug: #1904029
2020-11-16 10:35:41 +02:00
Dmitry Tantsur
8b98452cbb Refactor ironic authentication into a new module_utils module
This change merely moves the code to one location. The next logical
step would be to make IronicModule inherit the common ansible module.

Change-Id: Iec0ca1e33de6ebc36d7664941eafe1d77203d8f2
2020-10-26 11:05:31 +00:00
Zuul
d081bb5378 Merge "OpenStackModule: Support defining a minimum version of the SDK" 2020-10-23 17:11:01 +00:00
Sagi Shnaidman
2ce1adad4a Add galaxy.yml to support install from git
For installing collection from git like:
"ansible-galaxy collection install git+https://..." the galaxy.yml
file is required to be in the collection.
Add galaxy.yml with next version and "-dev".
Fix links for docs and issues.

Change-Id: I74863977732ebea9cd63ccdd2e830a6671a9e955
2020-10-21 09:12:05 +00:00
Mark Chappell
8ca8df1a84 OpenStackModule: Support defining a minimum version of the SDK
While it's currently possible to set min_ver and max_ver for specific
parameter, there are times when the whole module needs to specify a
minimum version:

- When the object isn't supported at all prior to a version
- When major features are missing from the SDK prior to a version

Change-Id: I94bbff7c54621e8a4786ebc7eb030103255dcb17
2020-10-19 13:55:30 +02:00
Riccardo Pittau
058cb4ff3f Migrate bifrost jobs to focal
Change-Id: Ic52dee90bc6b5d5e0ee1bc7fcbfa273867b937e2
2020-10-19 10:43:00 +00:00
Jesper Schmitz Mouridsen
ab96eb6a11 Refactor loadbalancer module
* enable check_mode
 * enable allowed_cidrs on listener if octavia version is >= 2.12
 * Only send flavor_id if it is not None

Change-Id: I4fd36bf3ed347e020151721a6b56d1cac0a8fd23
2020-10-12 19:08:45 +02:00
125 changed files with 7283 additions and 4194 deletions

1
.gitignore vendored
View File

@@ -1,7 +1,6 @@
.tox
build_artifact
ansible_collections
galaxy.yml
FILES.json
MANIFEST.json
importer_result.json

View File

@@ -8,11 +8,61 @@
using master of openstacksdk with latest ansible release
required-projects:
- openstack/ansible-collections-openstack
- openstack/designate
irrelevant-files: &ignore_files
- changelogs/.*
- COPYING
- docs/.*
- .*\.md
- .*\.rst
- tools/run-ansible-sanity.sh
- tests/sanity/.*
- contrib/.*
vars:
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
tox_envlist: ansible-2.9
tox_envlist: ansible
tox_install_siblings: true
fetch_subunit: false
devstack_plugins:
designate: https://opendev.org/openstack/designate
devstack_services:
designate: true
- job:
name: ansible-collections-openstack-functional-devstack-octavia
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
with Octavia plugin enabled, using releases of openstacksdk and latest
ansible release. Run it only on Load Balancer changes.
pre-run: ci/playbooks/get_amphora_tarball.yaml
required-projects:
- openstack/octavia
- name: github.com/ansible/ansible
override-checkout: stable-2.11
files:
- ^ci/roles/loadbalancer/.*$
- ^plugins/modules/lb_health_monitor.py
- ^plugins/modules/lb_listener.py
- ^plugins/modules/lb_member.py
- ^plugins/modules/lb_pool.py
- ^plugins/modules/loadbalancer.py
vars:
tox_envlist: ansible
devstack_plugins:
designate: https://opendev.org/openstack/designate
octavia: https://opendev.org/openstack/octavia
devstack_services:
designate: true
octavia: true
o-api: true
o-cw: true
o-hk: true
o-hm: true
devstack_localrc:
OCTAVIA_AMP_IMAGE_FILE: "/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
OCTAVIA_AMP_IMAGE_SIZE: 3
OCTAVIA_AMP_IMAGE_NAME: "test-only-amphora-x64-haproxy-ubuntu-bionic"
- job:
name: ansible-collections-openstack-functional-devstack-releases
@@ -21,21 +71,10 @@
Run openstack collections functional tests against a master devstack
using releases of openstacksdk and latest ansible release
vars:
tox_envlist: ansible-2.9
tox_envlist: ansible
tox_install_siblings: false
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.10
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and stable 2.10 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
vars:
tox_envlist: ansible
# Job with Ansible 2.9 for checking backward compatibility
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.9
parent: ansible-collections-openstack-functional-devstack
@@ -48,6 +87,18 @@
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.11
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and stable 2.11 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-ansible-devel
parent: ansible-collections-openstack-functional-devstack
@@ -58,120 +109,92 @@
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
tox_envlist: ansible-2.11
# Stable branches tests
- job:
name: ansible-collections-openstack-functional-devstack-wallaby-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a wallaby devstack
using wallaby brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/wallaby
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/wallaby
vars:
tox_envlist: ansible
# Pip installation job
- job:
name: ansible-collections-openstack-functional-devstack-ansible-pip
parent: ansible-collections-openstack-functional-devstack
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk with latest ansible release.
Ansible collection is built using PIP.
Run openstack collections functional tests against a victoria devstack
using victoria brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/victoria
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/victoria
vars:
tox_envlist: ansible-pip
tox_envlist: ansible
# Stable branches
- job:
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.9
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a ussuri devstack
using ussuri brach of openstacksdk and stable 2.9 branch of ansible
using ussuri brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/ussuri
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-branch: ussuri
- name: openstack/devstack
override-checkout: ussuri
override-checkout: stable/ussuri
- name: openstack/os-client-config
override-checkout: stable/ussuri
vars:
tox_envlist: ansible-2.9
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-train-ansible-2.9
name: ansible-collections-openstack-functional-devstack-train-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a train devstack
using train brach of openstacksdk and stable 2.9 branch of ansible
voting: true
using train brach of openstacksdk and stable 2.11 branch of ansible
override-checkout: stable/train
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-branch: train
- name: openstack/devstack
override-checkout: train
override-checkout: stable/train
- name: openstack/os-client-config
override-checkout: stable/train
vars:
tox_envlist: ansible-2.9
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-stein-ansible-2.9
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a stein devstack
using stein brach of openstacksdk and stable 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
- name: openstack/openstacksdk
override-branch: stein
- name: openstack/devstack
override-checkout: stein
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-rocky-ansible-2.9
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a rocky devstack
using rocky brach of openstacksdk and stable 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
- name: openstack/openstacksdk
override-branch: rocky
- name: openstack/devstack
override-checkout: rocky
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.9
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
using master brach of openstacksdk and stable 2.9 branch of ansible
using master branch of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/queens
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-branch: master
- name: openstack/devstack
override-checkout: queens
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
using master brach of openstacksdk and stable 2.10 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: master
- name: openstack/devstack
override-checkout: queens
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible
@@ -180,18 +203,57 @@
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
using master brach of openstacksdk and devel branch of ansible
voting: true
using master branch of openstacksdk and devel branch of ansible
voting: false
override-checkout: stable/queens
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
- name: openstack/openstacksdk
override-branch: master
- name: openstack/devstack
override-checkout: queens
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible-2.11
# Experimental pipeline jobs
- job:
name: ansible-collections-openstack-functional-devstack-stein-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a stein devstack
using stein brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/stein
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/stein
- name: openstack/os-client-config
override-checkout: stable/stein
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-rocky-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a rocky devstack
using rocky brach of openstacksdk and stable 2.11 branch of ansible
voting: true
override-checkout: stable/rocky
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.11
- name: openstack/openstacksdk
override-checkout: stable/rocky
- name: openstack/os-client-config
override-checkout: stable/rocky
- name: openstack/shade
override-checkout: stable/rocky
vars:
tox_envlist: ansible
# Linters
- job:
@@ -205,17 +267,19 @@
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
tox_envlist: linters-2.11
- job:
name: openstack-tox-linters-ansible-2.10
name: openstack-tox-linters-ansible-2.11
parent: openstack-tox-linters
nodeset: ubuntu-bionic
description: |
Run openstack collections linter tests using the 2.10 branch of ansible
voting: true
Run openstack collections linter tests using the 2.11 branch of ansible
voting: false
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
override-checkout: stable-2.11
- job:
name: openstack-tox-linters-ansible-2.9
@@ -233,70 +297,102 @@
# Cross-checks with other projects
- job:
name: bifrost-collections-src
parent: bifrost-integration-tinyipa-ubuntu-bionic
parent: bifrost-integration-tinyipa-ubuntu-focal
required-projects:
- openstack/ansible-collections-openstack
- job:
name: bifrost-keystone-collections-src
parent: bifrost-integration-tinyipa-keystone-ubuntu-bionic
parent: bifrost-integration-tinyipa-keystone-ubuntu-focal
required-projects:
- openstack/ansible-collections-openstack
# TripleO jobs
- job:
name: tripleo-ci-centos-8-standalone-osa
parent: tripleo-ci-centos-8-standalone
vars:
consumer_job: false
build_container_images: true
# Run only on files used in TripleO
files: &ooo_files
- ^plugins/modules/catalog_service.*$
- ^plugins/modules/endpoint.*$
- ^plugins/modules/identity_domain.*$
- ^plugins/modules/identity_domain_info.*$
- ^plugins/modules/identity_role.*$
- ^plugins/modules/identity_user.*$
- ^plugins/modules/keypair.*$
- ^plugins/modules/project.*$
- ^plugins/modules/role_assignment.*$
- ^plugins/modules/stack.*$
- ^plugins/module_utils/openstack.*$
- job:
name: tripleo-ci-centos-8-standalone-train-osa
parent: tripleo-ci-centos-8-standalone-osa
voting: false
override-checkout: stable/train
vars:
branch_override: stable/train
- job:
name: ansible-collections-openstack-release
parent: base
run: ci/publish/publish_collection.yml
secrets:
- ansible_galaxy_info
- secret:
name: ansible_galaxy_info
data:
url: https://galaxy.ansible.com
token: !encrypted/pkcs1-oaep
- lZFzfoCbuwqV1k6qRfl/VS7E+knUW7+zpg7BptrenK4n0g7UY0HtdVkYq0pV0Tj/LbhzG
jHD0mehcV1iS6B7ORKg4criJkdDfEx09BD8z8yv0EleiIMmhlrCoMcY593OZMBtVbGi0D
CwQtNO98QIsfZogChfLfvRNiBmUV98mEb/p6p3EtGx8J7qcAsqfWxc/CzB8GCleLAHHHT
FuikMM03ZnV0ew7E+TPkHbzzPhBZOqS5HYF0HtgttHwIXdfIWp/XdTuEEk7uRRgYZ2Iao
ifWRzoKaOQmhM++e1ydCqw9D4y9dZEFNMQLwSqcrvtb8cNwT1kl7SCFqYNE2lbutj4ne6
PTBQRsKegMB4Y3ena14fNF6tCynvJLPhF/cjPH2Jhs+B19XQhWkL3TgiOY02W24YHwRcP
+LdkM8inAvyVi3DEbEqdjBPO9OFJcBOKPlCdkGvuwdNCuEpEwctWs0gV3voflG2CDKzmJ
wu9JJOAWnq/0l1WpuDqWreKeQ/BUGZC2Gb4xRAqofulgvhs4WuYoEccjH4EJFIZ90S1EP
R/ZLadqZaEhmjwGM5sMWbBbjT23XsRgg0Tzt9m8DENYMuYDqkMdRbt2jYZa+32p4hyxVe
Y6H/pqYq5b9uOzumnShaK4WlmkQyXcNPkoSlMC1h4OGvqX/WUixpI38jyMA5Tc=
- project:
check:
jobs:
- tox-pep8
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.10
- openstack-tox-linters-ansible-2.11
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack:
dependencies: &deps_unit_lint
- tox-pep8
- openstack-tox-linters-ansible-2.9
irrelevant-files: &ignore_files
- changelogs/.*
- COPYING
- docs/.*
- .*\.md
- .*\.rst
- tools/run-ansible-sanity.sh
- tests/sanity/.*
- contrib/.*
# - openstack-tox-linters-ansible-2.11
- ansible-collections-openstack-functional-devstack:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-releases:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ansible-2.9:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ansible-2.10:
- ansible-collections-openstack-functional-devstack-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ansible-devel:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ansible-pip:
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.9:
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-train-ansible-2.9:
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-stein-ansible-2.9:
- ansible-collections-openstack-functional-devstack-train-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.9:
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-queens-ansible-2.9:
voting: false
- ansible-collections-openstack-functional-devstack-octavia:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- bifrost-collections-src:
voting: false
@@ -307,39 +403,56 @@
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- tripleo-ci-centos-8-standalone-osa:
dependencies: *deps_unit_lint
- tripleo-ci-centos-8-standalone-train-osa:
voting: false
dependencies: *deps_unit_lint
gate:
jobs:
- tox-pep8
- openstack-tox-linters-ansible-2.10
# - openstack-tox-linters-ansible-2.11
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.10
- ansible-collections-openstack-functional-devstack-ansible-pip
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.9
- ansible-collections-openstack-functional-devstack-train-ansible-2.9
- ansible-collections-openstack-functional-devstack-stein-ansible-2.9
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.9
- ansible-collections-openstack-functional-devstack-queens-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.11
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.11
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.11
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
# - ansible-collections-openstack-functional-devstack-queens-ansible-2.11
- ansible-collections-openstack-functional-devstack-octavia
- tripleo-ci-centos-8-standalone-osa
periodic:
jobs:
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.10
- openstack-tox-linters-ansible-2.11
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.10
- ansible-collections-openstack-functional-devstack-ansible-2.11
- ansible-collections-openstack-functional-devstack-ansible-devel
- ansible-collections-openstack-functional-devstack-ansible-pip
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.9
- ansible-collections-openstack-functional-devstack-train-ansible-2.9
- ansible-collections-openstack-functional-devstack-stein-ansible-2.9
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.9
- ansible-collections-openstack-functional-devstack-queens-ansible-2.9
- ansible-collections-openstack-functional-devstack-queens-ansible-2.10
- ansible-collections-openstack-functional-devstack-wallaby-ansible-2.11
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.11
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11
- ansible-collections-openstack-functional-devstack-train-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-devel
- bifrost-collections-src
- bifrost-keystone-collections-src
- ansible-collections-openstack-functional-devstack-octavia
experimental:
jobs:
- ansible-collections-openstack-functional-devstack-stein-ansible-2.11
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.11
- ansible-collections-openstack-functional-devstack-queens-ansible-devel
tag:
jobs:
- ansible-collections-openstack-release

View File

@@ -5,6 +5,176 @@ Openstack Cloud Ansilbe modules Release Notes
.. contents:: Topics
v1.5.0
======
Release Summary
---------------
New modules for DNS and FIPs and bugfixes.
Minor Changes
-------------
- Add bindep.txt for ansible-builder
- Add check_mode attribute to OpenstackModule
- Migrating image module from AnsibleModule to OpenStackModule
- Switch KeystoneFederationProtocolInfo module to OpenStackModule
- Switch ProjectAccess module to OpenStackModule
- Switch Quota module to OpenStackModule
- Switch Recordset module to OpenStackModule
- Switch ServerGroup module to OpenStackModule
- Switch ServerMetadata module to OpenStackModule
- Switch Snapshot module to OpenStackModule
- Switch Stack module to OpenStackModule
- Switch auth module to OpenStackModule
- Switch catalog_service module to OpenStackModule
- Switch coe_cluster module to OpenStackModule
- Switch coe_cluster_template module to OpenStackModule
- Switch endpoint module to OpenStackModule
- Switch federation_idp module to OpenStackModule
- Switch federation_idp_info module to OpenStackModule
- Switch federation_mapping module to OpenStackModule
- Switch federation_mapping_info module to OpenStackModule
- Switch federation_protocol module to OpenStackModule
- Switch flavor module to OpenStackModule
- Switch flavor_info module to OpenStackModule
- Switch floating_ip module to OpenStackModule
- Switch group_assignment module to OpenStackModule
- Switch hostaggregate module to OpenStackModule
- Switch identity_domain module to OpenStackModule
- Switch identity_domain_info module to OpenStackModule
- Switch identity_group module to OpenStackModule
- Switch identity_group_info module to OpenStackModule
- Switch identity_role module to OpenStackModule
- Switch identity_user module to OpenStackModule
- Switch lb_listener module to OpenStackModule
- Switch lb_member module to OpenStackModule
- Switch lb_pool module to OpenStackModule
- Switch object module to OpenStackModule
- Switch port module to OpenStackModule
- Switch port_info module to OpenStackModule
- Switch project and project_info module to OpenStackModule
- Switch role_assignment module to OpenStackModule
- Switch user_info module to OpenStackModule
- image - Add support to setting image tags
Bugfixes
--------
- Update checks for validate_certs in openstack_cloud_from_module
- compute_flavor - Fix the idempotent of compute_flavor module
- host_aggregate - Fix host_aggregate to tolerate aggregate.hosts being None
- inventory/openstack - Fix inventory plugin on Ansible 2.11
- port - fix update on empty list of allowed address pairs
- setup.cfg Replace dashes with underscores
- subnet - Only apply necessary changes to subnets
- volume - Fail if referenced source image for a new volume does not exist
New Modules
-----------
- openstack.cloud.dns_zone_info - Getting information about dns zones
- openstack.cloud.floating_ip_info - Get information about floating ips
- openstack.cloud.address_scope - Create or delete address scopes from OpenStack
v1.4.0
======
Release Summary
---------------
New object_container module and bugfixes.
Bugfixes
--------
- Add Octavia job for testing Load Balancer
- Add binding profile to port module
- Add execution environment metadata
- Fix CI for latest ansible-test with no_log
- Fix issues with newest ansible-test 2.11
- Prepare for Ansible 2.11 tests
- add option to exclude legacy groups
- security_group_rule add support ipv6-icmp
New Modules
-----------
- openstack.cloud.object_container - Manage Swift container
v1.3.0
======
Release Summary
---------------
New modules and bugfixes.
Minor Changes
-------------
- Fix some typos in readme
- Guidelines Fix links and formatting
- baremetal_node - Add support for new features
- baremetal_node - ironic deprecate sub-options of driver_info
- baremetal_node - ironic stop putting meaningless values to properties
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
- recordset - Update recordset docu
- server - Allow description field to be set with os_server
- server_action - Added shelve and unshelve as new server actions
Bugfixes
--------
- port - Fixed check for None in os_port
- project - Fix setting custom property on os_project
- security_group_rule - Remove protocols choice in security rules
- volume_info - Fix volume_info result for SDK < 0.19
New Modules
-----------
- openstack.cloud.identity_role_info - Retrieve information about Openstack Identity roles.
- openstack.cloud.keypair_info - Retrieve information about Openstack key pairs.
- openstack.cloud.security_group_info - Retrieve information about Openstack Security Groups.
- openstack.cloud.security_group_rule_info - Retrieve information about Openstack Security Group rules.
- openstack.cloud.stack_info - Retrieve information about Openstack Heat stacks.
v1.2.1
======
Release Summary
---------------
Porting modules to new OpenstackModule class and fixes.
Minor Changes
-------------
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
- dns_zone, recordset - Enable update for recordset and add tests for dns and recordset module
- endpoint - Do not fail when endpoint state is absent
- ironic - Refactor ironic authentication into a new module_utils module
- loadbalancer - Refactor loadbalancer module
- network - Migrating network from AnsibleModule to OpenStackModule
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
- openstack - Add galaxy.yml to support install from git
- openstack - Fix docs-args mismatch in modules
- openstack - OpenStackModule Support defining a minimum version of the SDK
- router - Migrating routers from AnsibleModule to OpenStackModule
- routers_info - Added deprecated_names for router_info module
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
- security_group_rule - Refactor TCP/UDP port check
- server.py - Improve "server" module with OpenstackModule class
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
- subnet - Fix subnets update and idempotency
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
- volume_info - Fix volume_info arguments for SDK 0.19
v1.2.0
======

View File

@@ -11,7 +11,7 @@ The collection includes the Openstack modules and plugins supported by Openstack
### Installing dependencies
For using Openstack Cloud collection firstly need to install `ansible` and `openstacksdk` Python modules on Ansible controller.
For using the Openstack Cloud collection firstly you need to install `ansible` and `openstacksdk` Python modules on your Ansible controller.
For example with pip:
```bash
@@ -19,8 +19,8 @@ pip install ansible openstacksdk
```
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).
Please note, that under some circumstances Ansible might invoke not standard Python interpreter on the target host.
Using Python verison 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
Please note, that under some circumstances Ansible might invoke a non-standard Python interpreter on the target host.
Using Python version 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
---
@@ -39,7 +39,7 @@ Before using the Openstack Cloud collection, you need to install the collection
`ansible-galaxy collection install openstack.cloud`
You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml` using the format:
You can also include it in a `requirements.yml` file and install it through `ansible-galaxy collection install -r requirements.yml` using the format:
```yaml
collections:
@@ -48,7 +48,7 @@ collections:
### Playbooks
To use a module from Openstack Cloud collection, please reference the full namespace, collection name, and modules name that you want to use:
To use a module from the Openstack Cloud collection, please reference the full namespace, collection name, and module name that you want to use:
```yaml
---
@@ -66,7 +66,7 @@ To use a module from Openstack Cloud collection, please reference the full names
volume_size: 75
```
Or you can add full namepsace and collecton name in the `collections` element:
Or you can add the full namespace and collection name in the `collections` element:
```yaml
---
@@ -114,7 +114,7 @@ TBD
## Communication
We have a dedicated Interest Group for Openstack Ansible modules.
You can find other people interested in this in `#openstack-ansible-sig` on Freenode IRC.
You can find other people interested in this in `#openstack-ansible-sig` on [OFTC IRC](https://www.oftc.net/).
## License

7
bindep.txt Normal file
View File

@@ -0,0 +1,7 @@
# This is a cross-platform list tracking distribution packages needed by tests;
# see https://docs.openstack.org/infra/bindep/ for additional information.
gcc [compile platform:centos-8 platform:rhel-8]
python38-cryptography [platform:centos-8 platform:rhel-8]
python38-devel [compile platform:centos-8 platform:rhel-8]
python38-requests [platform:centos-8 platform:rhel-8]

View File

@@ -1,84 +1,234 @@
ancestor: null
releases:
1.2.0:
release_date: '2020-10-13'
changes:
release_summary: New volume backup modules.
minor_changes:
- lb_health_monitor - Make it possible to create a health monitor to a pool
modules:
- name: volume_snapshot_info module
description: Retrieve information about Openstack volume snapshots.
namespace: ''
- name: volume_backup_info module
description: Retrieve information about Openstack volume backups.
namespace: ''
- name: volume_backup module
description: Add/Delete Openstack volumes backup.
namespace: ''
1.1.0:
release_date: '2020-08-17'
changes:
release_summary: Starting redesign modules and bugfixes.
minor_changes:
- A basic module subclass was introduced and a few modules moved to inherit from it.
- Added pip installation option for collection.
- Added template for generation of artibtrary module.
- Add more useful information from exception
- inventory_openstack - Add openstack logger and Ansible display utility
- loadbalancer - Add support for setting the Flavor when creating a load balancer
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is provided
bugfixes:
- security_group_rule - Don't pass tenant_id for remote group
- Fix non existing attribuites in SDK exception
modules:
- name: volume_info
description: Retrieve information about Openstack volumes.
namespace: ''
1.0.1:
release_date: '2020-05-22'
changes:
release_summary: Bugfix for server_info
bugfixes:
- server_info - Fix broken server_info module and add tests
1.0.0:
release_date: '2020-05-19'
changes:
release_summary: Initial release of collection.
minor_changes:
- Renaming all modules and removing "os" prefix from names.
- server_action - pass imageRef to rebuild
- subnet - Updated allocation pool checks
- project - Add properties for os_project
- config - Update os_client_config to use openstacksdk
- host_aggregate - Add support for not 'purging' missing hosts
- baremetal_node_action - Support json type for the ironic_node config_drive parameter
bugfixes:
- federation_mapping - Fixup some minor nits found in followup reviews
- coe_cluster - Retrive id/uuid correctly
- baremetal_node - Correct parameter name
- network - Bump minimum openstacksdk version when using os_network/dns_domain
- inventory_openstack - Fix constructed compose
- role_assignment - Fix os_user_role for groups in multidomain context
- role_assignment - Fix os_user_role issue to grant a role in a domain
- baremetal_node - Correct parameter name
- coe_cluster - Retrive id/uuid correctly
- federation_mapping - Fixup some minor nits found in followup reviews
- inventory_openstack - Fix constructed compose
- network - Bump minimum openstacksdk version when using os_network/dns_domain
- role_assignment - Fix os_user_role for groups in multidomain context
- role_assignment - Fix os_user_role issue to grant a role in a domain
minor_changes:
- Renaming all modules and removing "os" prefix from names.
- baremetal_node_action - Support json type for the ironic_node config_drive
parameter
- config - Update os_client_config to use openstacksdk
- host_aggregate - Add support for not 'purging' missing hosts
- project - Add properties for os_project
- server_action - pass imageRef to rebuild
- subnet - Updated allocation pool checks
release_summary: Initial release of collection.
modules:
- name: keystone_federation_protocol_info
description: Add support for getting information about Keystone federation Protocols
namespace: ''
- name: keystone_federation_protocol
description: Add support for Keystone federation Protocols
namespace: ''
- name: federation_idp_info
description: Add support for fetching the information about federation IDPs
namespace: ''
- name: federation_idp
description: Add support for Keystone Identity Providers
namespace: ''
- name: federation_mapping_info
description: Add support for fetching the information about Keystone mappings
namespace: ''
- name: federation_mapping
description: Add support for Keystone mappings
namespace: ''
- name: routers_info
description: Retrieve information about one or more OpenStack routers.
namespace: ''
- description: Add support for Keystone Identity Providers
name: federation_idp
namespace: ''
- description: Add support for fetching the information about federation IDPs
name: federation_idp_info
namespace: ''
- description: Add support for Keystone mappings
name: federation_mapping
namespace: ''
- description: Add support for fetching the information about Keystone mappings
name: federation_mapping_info
namespace: ''
- description: Add support for Keystone federation Protocols
name: keystone_federation_protocol
namespace: ''
- description: Add support for getting information about Keystone federation Protocols
name: keystone_federation_protocol_info
namespace: ''
- description: Retrieve information about one or more OpenStack routers.
name: routers_info
namespace: ''
release_date: '2020-05-19'
1.0.1:
changes:
bugfixes:
- server_info - Fix broken server_info module and add tests
release_summary: Bugfix for server_info
release_date: '2020-05-22'
1.1.0:
changes:
bugfixes:
- Fix non existing attribuites in SDK exception
- security_group_rule - Don't pass tenant_id for remote group
minor_changes:
- A basic module subclass was introduced and a few modules moved to inherit
from it.
- Add more useful information from exception
- Added pip installation option for collection.
- Added template for generation of artibtrary module.
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is
provided
- inventory_openstack - Add openstack logger and Ansible display utility
- loadbalancer - Add support for setting the Flavor when creating a load balancer
release_summary: Starting redesign modules and bugfixes.
modules:
- description: Retrieve information about Openstack volumes.
name: volume_info
namespace: ''
release_date: '2020-08-17'
1.2.0:
changes:
minor_changes:
- lb_health_monitor - Make it possible to create a health monitor to a pool
release_summary: New volume backup modules.
modules:
- description: Add/Delete Openstack volumes backup.
name: volume_backup module
namespace: ''
- description: Retrieve information about Openstack volume backups.
name: volume_backup_info module
namespace: ''
- description: Retrieve information about Openstack volume snapshots.
name: volume_snapshot_info module
namespace: ''
release_date: '2020-10-13'
1.2.1:
changes:
minor_changes:
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
- dns_zone, recordset - Enable update for recordset and add tests for dns and
recordset module
- endpoint - Do not fail when endpoint state is absent
- ironic - Refactor ironic authentication into a new module_utils module
- loadbalancer - Refactor loadbalancer module
- network - Migrating network from AnsibleModule to OpenStackModule
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
- openstack - Add galaxy.yml to support install from git
- openstack - Fix docs-args mismatch in modules
- openstack - OpenStackModule Support defining a minimum version of the SDK
- router - Migrating routers from AnsibleModule to OpenStackModule
- routers_info - Added deprecated_names for router_info module
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
- security_group_rule - Refactor TCP/UDP port check
- server.py - Improve "server" module with OpenstackModule class
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
- subnet - Fix subnets update and idempotency
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
- volume_info - Fix volume_info arguments for SDK 0.19
release_summary: Porting modules to new OpenstackModule class and fixes.
release_date: '2021-01-03'
1.3.0:
changes:
bugfixes:
- port - Fixed check for None in os_port
- project - Fix setting custom property on os_project
- security_group_rule - Remove protocols choice in security rules
- volume_info - Fix volume_info result for SDK < 0.19
minor_changes:
- Fix some typos in readme
- Guidelines Fix links and formatting
- baremetal_node - Add support for new features
- baremetal_node - ironic deprecate sub-options of driver_info
- baremetal_node - ironic stop putting meaningless values to properties
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
- recordset - Update recordset docu
- server - Allow description field to be set with os_server
- server_action - Added shelve and unshelve as new server actions
release_summary: New modules and bugfixes.
modules:
- description: Retrieve information about Openstack Identity roles.
name: identity_role_info
namespace: ''
- description: Retrieve information about Openstack key pairs.
name: keypair_info
namespace: ''
- description: Retrieve information about Openstack Security Groups.
name: security_group_info
namespace: ''
- description: Retrieve information about Openstack Security Group rules.
name: security_group_rule_info
namespace: ''
- description: Retrieve information about Openstack Heat stacks.
name: stack_info
namespace: ''
release_date: '2021-02-16'
1.4.0:
changes:
bugfixes:
- Add Octavia job for testing Load Balancer
- Add binding profile to port module
- Add execution environment metadata
- Fix CI for latest ansible-test with no_log
- Fix issues with newest ansible-test 2.11
- Prepare for Ansible 2.11 tests
- add option to exclude legacy groups
- security_group_rule add support ipv6-icmp
release_summary: New object_container module and bugfixes.
modules:
- description: Manage Swift container
name: object_container
namespace: ''
release_date: '2021-04-08'
1.5.0:
changes:
bugfixes:
- Update checks for validate_certs in openstack_cloud_from_module
- compute_flavor - Fix the idempotent of compute_flavor module
- host_aggregate - Fix host_aggregate to tolerate aggregate.hosts being None
- inventory/openstack - Fix inventory plugin on Ansible 2.11
- port - fix update on empty list of allowed address pairs
- setup.cfg Replace dashes with underscores
- subnet - Only apply necessary changes to subnets
- volume - Fail if referenced source image for a new volume does not exist
minor_changes:
- Add bindep.txt for ansible-builder
- Add check_mode attribute to OpenstackModule
- Migrating image module from AnsibleModule to OpenStackModule
- Switch KeystoneFederationProtocolInfo module to OpenStackModule
- Switch ProjectAccess module to OpenStackModule
- Switch Quota module to OpenStackModule
- Switch Recordset module to OpenStackModule
- Switch ServerGroup module to OpenStackModule
- Switch ServerMetadata module to OpenStackModule
- Switch Snapshot module to OpenStackModule
- Switch Stack module to OpenStackModule
- Switch auth module to OpenStackModule
- Switch catalog_service module to OpenStackModule
- Switch coe_cluster module to OpenStackModule
- Switch coe_cluster_template module to OpenStackModule
- Switch endpoint module to OpenStackModule
- Switch federation_idp module to OpenStackModule
- Switch federation_idp_info module to OpenStackModule
- Switch federation_mapping module to OpenStackModule
- Switch federation_mapping_info module to OpenStackModule
- Switch federation_protocol module to OpenStackModule
- Switch flavor module to OpenStackModule
- Switch flavor_info module to OpenStackModule
- Switch floating_ip module to OpenStackModule
- Switch group_assignment module to OpenStackModule
- Switch hostaggregate module to OpenStackModule
- Switch identity_domain module to OpenStackModule
- Switch identity_domain_info module to OpenStackModule
- Switch identity_group module to OpenStackModule
- Switch identity_group_info module to OpenStackModule
- Switch identity_role module to OpenStackModule
- Switch identity_user module to OpenStackModule
- Switch lb_listener module to OpenStackModule
- Switch lb_member module to OpenStackModule
- Switch lb_pool module to OpenStackModule
- Switch object module to OpenStackModule
- Switch port module to OpenStackModule
- Switch port_info module to OpenStackModule
- Switch project and project_info module to OpenStackModule
- Switch role_assignment module to OpenStackModule
- Switch user_info module to OpenStackModule
- image - Add support to setting image tags
release_summary: New modules for DNS and FIPs and bugfixes.
modules:
- description: Getting information about dns zones
name: dns_zone_info
namespace: ''
- description: Get information about floating ips
name: floating_ip_info
namespace: ''
- description: Create or delete address scopes from OpenStack
name: address_scope
namespace: ''
release_date: '2021-06-23'

View File

@@ -0,0 +1,6 @@
- hosts: all
tasks:
- name: Download amphora tarball
get_url:
url: "https://tarballs.openstack.org/octavia/test-images/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
dest: /tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2

View File

@@ -0,0 +1,75 @@
---
- hosts: all
vars:
collection_path: "{{ ansible_user_dir }}/{{ zuul.projects['opendev.org/openstack/ansible-collections-openstack'].src_dir }}"
build_collection_path: /tmp/collection_built/
ansible_galaxy_path: "~/.local/bin/ansible-galaxy"
tasks:
- name: Include role for pip
include_role:
name: ensure-pip
- name: Install ansible
pip:
name: ansible-core<2.12
- name: Discover tag version
set_fact:
version_tag: "{{ zuul.tag|default('no_version', true) }}"
- name: Fail if no tag version found
fail:
msg: "No tag was found in Zuul vars!"
when: version_tag == 'no_version'
- name: Create a directory for collection
file:
state: "{{ item }}"
path: "{{ build_collection_path }}"
loop:
- absent
- directory
- name: Set galaxy.yml for right version from tag
lineinfile:
path: '{{ collection_path }}/galaxy.yml'
regexp: '^version:.*'
line: 'version: {{ version_tag }}'
- name: Build collection
command: "{{ ansible_galaxy_path }} collection build --output-path {{ build_collection_path }} --force"
args:
chdir: "{{ collection_path }}"
- name: Publish content to Ansible Galaxy
block:
- name: Create ansible.cfg configuration file tempfile
tempfile:
state: file
suffix: .cfg
register: _ansiblecfg_tmp
- name: Create ansible.cfg configuration file
copy:
dest: "{{ _ansiblecfg_tmp.path }}"
mode: 0600
content: |
[galaxy]
server_list = release_galaxy
[galaxy_server.release_galaxy]
url = {{ ansible_galaxy_info.url }}
token = {{ ansible_galaxy_info.token }}
- name: Publish collection to Ansible Galaxy / Automation Hub
environment:
ANSIBLE_CONFIG: "{{ _ansiblecfg_tmp.path }}"
shell: >-
{{ ansible_galaxy_path }} collection publish -vvv
{{ build_collection_path }}/openstack-cloud-{{ version_tag }}.tar.gz
always:
- name: Shred ansible-galaxy credentials
command: "shred {{ _ansiblecfg_tmp.path }}"

View File

@@ -0,0 +1 @@
address_scope_name: "adrdess_scope"

View File

@@ -0,0 +1,40 @@
---
- name: Create address_scope
openstack.cloud.address_scope:
cloud: "{{ cloud }}"
name: "{{ address_scope_name }}"
shared: False
ip_version: "4"
register: create_address_scope
- name: Verify address scope
assert:
that:
- create_address_scope is successful
- create_address_scope is changed
- create_address_scope.address_scope.name == address_scope_name
- create_address_scope.address_scope.is_shared == False
- create_address_scope.address_scope.ip_version == 4
- name: Update address scope
openstack.cloud.address_scope:
cloud: "{{ cloud }}"
name: "{{ address_scope_name }}"
shared: True
ip_version: "4"
register: update_address_scope
- name: Verify updated IPv4 address scope
assert:
that:
- update_address_scope is successful
- update_address_scope is changed
- update_address_scope.address_scope.name == address_scope_name
- update_address_scope.address_scope.is_shared == True
- update_address_scope.address_scope.ip_version == 4
- name: Delete created address scope
openstack.cloud.address_scope:
cloud: "{{ cloud }}"
name: "{{ address_scope_name }}"
state: absent

View File

@@ -0,0 +1,4 @@
dns_zone_name: test.dns.zone.
recordset_name: testrecordset.test.dns.zone.
records: ['10.0.0.0']
updated_records: ['10.1.1.1']

View File

@@ -0,0 +1,79 @@
---
- name: Create dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ dns_zone_name }}"
zone_type: "primary"
email: test@example.net
register: dns_zone
- debug: var=dns_zone
- name: Update dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ dns_zone.zone.name }}"
description: "New descirption"
register: updated_dns_zone
- debug: var=updated_dns_zone
- name: Create a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ records }}"
register: recordset
- name: Verify recordset info
assert:
that:
- recordset["recordset"].name == recordset_name
- recordset["recordset"].zone_name == dns_zone.zone.name
- recordset["recordset"].records == records
- name: Update a recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset_name }}"
recordset_type: "a"
records: "{{ updated_records }}"
description: "new test recordset"
register: updated_recordset
- name: Verify recordset info
assert:
that:
- updated_recordset["recordset"].zone_name == dns_zone.zone.name
- updated_recordset["recordset"].name == recordset_name
- updated_recordset["recordset"].records == updated_records
- name: Delete recordset
openstack.cloud.recordset:
cloud: "{{ cloud }}"
zone: "{{ updated_dns_zone.zone.name }}"
name: "{{ recordset.recordset.name }}"
state: absent
register: deleted_recordset
- name: Verify recordset deletion
assert:
that:
- deleted_recordset is successful
- deleted_recordset is changed
- name: Delete dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ updated_dns_zone.zone.name }}"
state: absent
register: deleted_dns_zone
- name: Verify dns zone
assert:
that:
- deleted_dns_zone is successful
- deleted_dns_zone is changed

View File

@@ -0,0 +1,41 @@
---
- name: Set random prefix
set_fact:
prefix: "{{ 99999999 | random | to_uuid | hash('md5') }}"
- name: Create dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ (prefix + '.test.zone.') }}"
email: test@example.net
- name: Getting info about dns zones
openstack.cloud.dns_zone_info:
cloud: "{{ cloud }}"
register: zones
- name: assert result
assert:
that:
- zones is success
- zones is not changed
- zones | length > 0
- name: Getting info about created zone
openstack.cloud.dns_zone_info:
cloud: "{{ cloud }}"
name: "{{ (prefix + '.test.zone.') }}"
register: zone
- name: assert result
assert:
that:
- zone is success
- zone is not changed
- zone.zones | length == 1
- name: Drop created dns zone
openstack.cloud.dns_zone:
cloud: "{{ cloud }}"
name: "{{ (prefix + '.test.zone.') }}"
state: absent

View File

@@ -0,0 +1,11 @@
---
- name: Getting info about allocated ips
openstack.cloud.floating_ip_info:
cloud: "{{ cloud }}"
register: fips
- name: assert result
assert:
that:
- fips is success
- fips is not changed

View File

@@ -1 +1,4 @@
image_name: ansible_image
image_tags:
- test
- ansible

View File

@@ -13,9 +13,20 @@
name: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags: "{{ image_tags }}"
register: image
- debug: var=image
- name: Get details of created image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: image_info_result
- name: Verify image info
assert:
that:
- "image_info_result.openstack_image.name == image_name"
- "image_info_result.openstack_image.tags | sort == image_tags | sort"
- name: Delete raw image (defaults)
openstack.cloud.image:
@@ -40,8 +51,6 @@
distro: ubuntu
register: image
- debug: var=image
- name: Delete raw image (complex)
openstack.cloud.image:
cloud: "{{ cloud }}"
@@ -52,3 +61,14 @@
file:
name: "{{ tmp_file.stdout }}"
state: absent
- name: Try to get details of deleted image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: deleted_image_info_result
- name: Verify image is deleted
assert:
that:
- not deleted_image_info_result.openstack_image

View File

@@ -7,6 +7,17 @@
register:
keypair
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs contains single element
assert:
that:
- keypairs['openstack_keypairs']|length == 1
# This assert verifies that Ansible is capable serializing data returned by SDK
- name: Ensure private key is returned
assert:
@@ -19,6 +30,17 @@
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs is empty
assert:
that:
- keypairs['openstack_keypairs']|length == 0
- name: Generate test key file
user:
name: "{{ ansible_env.USER }}"
@@ -32,12 +54,34 @@
state: present
public_key_file: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs contains single element
assert:
that:
- keypairs['openstack_keypairs']|length == 1
- name: Delete keypair (file)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs is empty
assert:
that:
- keypairs['openstack_keypairs']|length == 0
- name: Create keypair (key)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
@@ -45,12 +89,34 @@
state: present
public_key: "{{ lookup('file', '~/.ssh/shade_id_rsa.pub') }}"
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs contains single element
assert:
that:
- keypairs['openstack_keypairs']|length == 1
- name: Delete keypair (key)
openstack.cloud.keypair:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
state: absent
- name: Get list of keypairs
openstack.cloud.keypair_info:
cloud: "{{ cloud }}"
name: "{{ keypair_name }}"
register: keypairs
- name: Ensure that list of keypairs is empty
assert:
that:
- keypairs['openstack_keypairs']|length == 0
- name: Delete test key pub file
file:
name: "{{ ansible_env.HOME }}/.ssh/shade_id_rsa.pub"

View File

@@ -5,6 +5,29 @@
state: present
name: "{{ role_name }}"
- name: List keystone roles
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
register: roles
- name: Check roles
assert:
that:
- roles.openstack_roles | length > 0
- "'{{ role_name }}' in (roles.openstack_roles | map(attribute='name') | list)"
- name: List keystone roles by name
openstack.cloud.identity_role_info:
cloud: "{{ cloud }}"
name: "{{ role_name}}"
register: roles1
- name: Check roles
assert:
that:
- roles1.openstack_roles | length == 1
- roles1.openstack_roles[0]['name'] == role_name
- name: Delete keystone role
openstack.cloud.identity_role:
cloud: "{{ cloud }}"

View File

@@ -0,0 +1,3 @@
network_name: network_lb
subnet_name: subnet_lb
lb_name: test_lb

View File

@@ -0,0 +1,50 @@
---
- name: Create network {{ network_name }} for LB
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: present
- name: Create subnet {{ subnet_name }} on network {{ network_name }} for LB
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
name: "{{ subnet_name }}"
state: present
enable_dhcp: true
dns_nameservers:
- 8.8.8.7
- 8.8.8.8
cidr: 192.168.0.0/24
gateway_ip: 192.168.0.1
allocation_pool_start: 192.168.0.2
allocation_pool_end: 192.168.0.254
- name: Create loadbalancer - generic
openstack.cloud.loadbalancer:
cloud: "{{ cloud }}"
state: present
name: "{{ lb_name }}"
vip_subnet: "{{ subnet_name }}"
timeout: 450
- name: Delete loadbalancer
openstack.cloud.loadbalancer:
cloud: "{{ cloud }}"
state: absent
name: "{{ lb_name }}"
timeout: 150
- name: Delete subnet {{ subnet_name }} on network {{ network_name }}
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
name: "{{ subnet_name }}"
state: absent
- name: Delete network {{ network_name }} of LB
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
state: absent

View File

@@ -1,3 +1,7 @@
network_name: shade_network
network_name_newparams: newparams_network
network_shared: false
network_external: false
dns_domain: example.opendev.org
mtu: 1250
port_security_enabled: false

View File

@@ -1,5 +1,5 @@
---
- name: Create network
- name: Create network - generic
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
@@ -7,8 +7,71 @@
shared: "{{ network_shared }}"
external: "{{ network_external }}"
- name: Delete network
openstack.cloud.network:
- name: Gather networks info - generic
openstack.cloud.networks_info:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
filters:
shared: "{{ network_shared|string|capitalize }}"
register: result
- name: Verify networks info - generic
assert:
that:
- result.openstack_networks.0.name == network_name
- (result.openstack_networks.0.shared|lower) == (network_shared|lower)
- result.openstack_networks[0]['router:external'] == {{ network_external }}
- name: Create network - with new SDK params
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ network_name_newparams }}"
state: present
shared: "{{ network_shared }}"
external: "{{ network_external }}"
mtu: "{{ mtu }}"
port_security_enabled: "{{ port_security_enabled }}"
register: result_create_nw_with_new_params
ignore_errors: yes
- name: Check errors below min sdk version - with new SDK params
assert:
that:
- result_create_nw_with_new_params.failed
- '"the installed version of the openstacksdk library MUST be >=0.18.0." in result_create_nw_with_new_params.msg'
when: sdk_version is version('0.18', '<')
- name: Gather networks info - with new SDK params
openstack.cloud.networks_info:
cloud: "{{ cloud }}"
name: "{{ network_name_newparams }}"
register: result_newparams
when: sdk_version is version('0.18', '>=')
- name: Verify networks info - with new SDK params
assert:
that:
- result_newparams.openstack_networks.0.name == network_name_newparams
- result_newparams.openstack_networks.0.mtu == mtu
- result_newparams.openstack_networks.0.port_security_enabled == port_security_enabled
when: sdk_version is version('0.18', '>=')
- name: Delete network - generic and with new SDK params
openstack.cloud.network:
cloud: "{{ cloud }}"
name: "{{ item }}"
state: absent
with_items:
- "{{ network_name }}"
- "{{ network_name_newparams }}"
- name: Gather networks info - deleted
openstack.cloud.networks_info:
cloud: "{{ cloud }}"
name: "{{ network_name }}"
register: result_nonet
- name: Verify networks info - deleted
assert:
that:
- result_nonet.openstack_networks == []

View File

@@ -0,0 +1 @@
container_name: "test-container"

View File

@@ -0,0 +1,60 @@
---
- module_defaults:
openstack.cloud.object_container:
cloud: "{{ cloud }}"
block:
- name: Create an empty container
openstack.cloud.object_container:
container: "{{ container_name }}"
register: container
- name: Verify container was created
assert:
that:
- container is success
- container is changed
- container.container.name == container_name
- name: Set metadata for a container
openstack.cloud.object_container:
container: "{{ container_name }}"
metadata: "Cache-Control='no-cache'"
register: set_meta
- name: Verify container metadata was set
assert:
that:
- set_meta is success
- set_meta is changed
- name: Delete some keys from container metadata
openstack.cloud.object_container:
container: "{{ container_name }}"
keys:
- Cache-Control
register: delete_meta
- name: Verify some keys from container metadata was deleted
assert:
that:
- delete_meta is success
- delete_meta is changed
- name: Delete container
openstack.cloud.object_container:
container: "{{ container_name }}"
state: absent
register: deleted
- name: Verify container was deleted
assert:
that:
- deleted is success
- deleted is changed
always:
- name: Delete container
openstack.cloud.object_container:
container: "{{ container_name }}"
state: absent
ignore_errors: yes

View File

@@ -0,0 +1,2 @@
---
stack_name: "test-stack"

View File

@@ -0,0 +1,11 @@
#
# Minimal HOT template defining a single compute server.
#
heat_template_version: 2013-05-23
description: >
Minimal HOT template for stack
parameters:
resources:
outputs:

View File

@@ -0,0 +1,44 @@
---
- name: Create minimal stack
openstack.cloud.stack:
cloud: "{{ cloud }}"
# template is searched related to playbook location or as absolute path
template: "roles/orchestration/files/hello-world.yaml"
name: "{{ stack_name }}"
- name: List stacks
openstack.cloud.stack_info:
cloud: "{{ cloud }}"
register: stacks
- assert:
that:
- stacks['stacks']|length > 0
- name: Get Single stack
openstack.cloud.stack_info:
cloud: "{{ cloud }}"
name: "{{ stack_name }}"
register: test_stack
- assert:
that:
- test_stack is defined
- test_stack['stacks'][0]['name'] == stack_name
- name: Delete stack
openstack.cloud.stack:
cloud: "{{ cloud }}"
name: "{{ stack_name }}"
state: absent
- name: Get Single stack
openstack.cloud.stack_info:
cloud: "{{ cloud }}"
name: "{{ stack_name }}"
register: stacks
- assert:
that:
- stacks is defined
- stacks['stacks']|length == 0

View File

@@ -4,3 +4,6 @@ subnet_name: ansible_port_subnet
port_name: ansible_port
secgroup_name: ansible_port_secgroup
no_security_groups: True
binding_profile:
"pci_slot": "0000:03:11.1"
"physical_network": "provider"

View File

@@ -88,6 +88,30 @@
state: absent
name: "{{ secgroup_name }}"
- name: Test port binding config (runs from train release sdk > 0.28)
block:
- name: Create port (with binding profile)
openstack.cloud.port:
cloud: "{{ cloud }}"
state: present
name: "{{ port_name }}"
network: "{{ network_name }}"
binding_profile: "{{ binding_profile }}"
register: port
- name: Assert binding:profile exists in created port
assert:
that: "port.port['binding:profile']"
- debug: var=port
- name: Delete port (with binding profile)
openstack.cloud.port:
cloud: "{{ cloud }}"
state: absent
name: "{{ port_name }}"
when: sdk_version is version(0.28, '>')
- name: Delete subnet
openstack.cloud.subnet:
cloud: "{{ cloud }}"

View File

@@ -32,6 +32,26 @@
protocol: tcp
remote_ip_prefix: 0.0.0.0/0
- name: Create TCP rule again with port range (1, 65535)
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
protocol: tcp
port_range_min: 1
port_range_max: 65535
remote_ip_prefix: 0.0.0.0/0
- name: Create TCP rule again with port range (-1, -1)
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
protocol: tcp
port_range_min: -1
port_range_max: -1
remote_ip_prefix: 0.0.0.0/0
- name: Create empty UDP rule
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
@@ -40,6 +60,26 @@
protocol: udp
remote_ip_prefix: 0.0.0.0/0
- name: Create UDP rule again with port range (1, 65535)
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
protocol: udp
port_range_min: 1
port_range_max: 65535
remote_ip_prefix: 0.0.0.0/0
- name: Create UDP rule again with port range (-1, -1)
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
state: present
protocol: udp
port_range_min: -1
port_range_max: -1
remote_ip_prefix: 0.0.0.0/0
- name: Create HTTP rule
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"
@@ -61,6 +101,71 @@
remote_ip_prefix: 0.0.0.0/0
direction: egress
- name: List all available rules of all security groups in a project
openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
when: sdk_version is version("0.32", '>=')
register: test_sec_rules
- name: Check - List all available rules of all security groups in a project
assert:
that:
- test_sec_rules.security_group_rules | length > 0
when: sdk_version is version("0.32", '>=')
- name: List all available rules of a specific security group
openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
register: test_sec_rule1
- name: Check - List all available rules of a specific security group
assert:
that:
- test_sec_rule1.security_group_rules | length > 0
- name: List all available rules with filters
openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
security_group: "{{ secgroup_name }}"
protocol: tcp
port_range_min: 80
port_range_max: 80
remote_ip_prefix: 0.0.0.0/0
when: sdk_version is version("0.32", '>=')
register: test_sec_rule
- name: Check - List all available rules with filters
assert:
that:
- test_sec_rule.security_group_rules | length == 1
when: sdk_version is version("0.32", '>=')
- name: List all security groups of a project
openstack.cloud.security_group_info:
cloud: "{{ cloud }}"
register: test_sec_groups
- name: Check - List all security groups of a project
assert:
that:
- test_sec_groups.security_groups | length > 0
- name: Filter security group by name
openstack.cloud.security_group_info:
cloud: "{{ cloud }}"
name: "{{ secgroup_name }}"
register: test_sec_group
- name: Check - List all security groups of a project
assert:
that:
- test_sec_group.security_groups | length == 1
- test_sec_group.security_groups[0]['name'] == secgroup_name
# This fails on Stein only
when: sdk_version is version("0.36.5", '>=')
- name: Delete empty ICMP rule
openstack.cloud.security_group_rule:
cloud: "{{ cloud }}"

View File

@@ -124,6 +124,33 @@
terminate_volume: true
wait: true
register: server
tags:
- object
- debug: var=server
tags:
- object
- name: Delete server with volume
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"
wait: true
tags:
- object
- name: Create a minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
wait: true
register: server
- debug: var=server
@@ -150,7 +177,7 @@
that:
info.openstack_servers|length > 0
- name: Delete server with volume
- name: Delete minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent

View File

@@ -200,7 +200,10 @@
that:
- info10.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- info10.openstack_servers.0.locked in (None, True)
- >-
(info10.openstack_server[0]['locked'] is defined and
info10.openstack_server[0]['locked']|bool) or
(info10.openstack_server[0]['locked'] is not defined)
- server is changed
- name: Lock server again
@@ -222,7 +225,10 @@
that:
- info11.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- info11.openstack_servers.0.locked in (None, True)
- >-
(info11.openstack_server[0]['locked'] is defined and
info11.openstack_server[0]['locked']|bool) or
(info11.openstack_server[0]['locked'] is not defined)
- server is changed # no support for lock idempotency
- name: Unock server
@@ -244,7 +250,10 @@
that:
- info12.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- info12.openstack_servers.0.locked in (None, False)
- >-
(info12.openstack_server[0]['locked'] is defined and
not info12.openstack_server[0]['locked']|bool) or
(info12.openstack_server[0]['locked'] is not defined)
- server is changed
- name: Unlock server again
@@ -267,7 +276,10 @@
- info13.openstack_servers.0.status == 'ACTIVE'
- server is changed # no support for unlock idempotency
# not in all versions 'locked' is supported
- info13.openstack_servers.0.locked in (None, False)
- >-
(info13.openstack_server[0]['locked'] is defined and
not info13.openstack_server[0]['locked']|bool) or
(info13.openstack_server[0]['locked'] is not defined)
- name: Suspend server
openstack.cloud.server_action:
@@ -406,3 +418,103 @@
that:
- info19.openstack_servers.0.status in ('ACTIVE', 'REBUILD')
- server is changed
- name: Shelve server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info20
- name: Ensure status for server is SHELVED or SHELVED_OFFLOADED
assert:
that:
- info20.openstack_servers.0.status in ['SHELVED', 'SHELVED_OFFLOADED']
- server is changed
- name: Shelve offload server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve_offload
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info21
- name: Ensure status for server is SHELVED_OFFLOADED
# no change if server has been offloaded automatically after first shelve command
assert:
that:
- info21.openstack_servers.0.status == 'SHELVED_OFFLOADED'
- name: Shelve offload server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve_offload
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info22
- name: Ensure status for server is SHELVED_OFFLOADED
assert:
that:
- info22.openstack_servers.0.status == 'SHELVED_OFFLOADED'
- server is not changed
- name: Unshelve server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unshelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info23
- name: Ensure status for server is ACTIVE
assert:
that:
- info23.openstack_servers.0.status == 'ACTIVE'
- server is changed
- name: Unshelve server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unshelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info24
- name: Ensure status for server is ACTIVE
assert:
that:
- info24.openstack_servers.0.status == 'ACTIVE'
- server is not changed

View File

@@ -17,6 +17,19 @@
allocation_pool_start: 192.168.0.2
allocation_pool_end: 192.168.0.4
- name: Create subnet {{ subnet_name }} on network {{ network_name }} again
openstack.cloud.subnet:
cloud: "{{ cloud }}"
network_name: "{{ network_name }}"
enable_dhcp: "{{ enable_subnet_dhcp }}"
name: "{{ subnet_name }}"
state: present
cidr: 192.168.0.0/24
gateway_ip: 192.168.0.1
allocation_pool_start: 192.168.0.2
allocation_pool_end: 192.168.0.4
register: idem1
- name: Update subnet {{ subnet_name }} allocation pools
openstack.cloud.subnet:
cloud: "{{ cloud }}"
@@ -24,7 +37,8 @@
name: "{{ subnet_name }}"
state: present
cidr: 192.168.0.0/24
allocation_pool_start: 192.168.0.5
gateway_ip: 192.168.0.1
allocation_pool_start: 192.168.0.2
allocation_pool_end: 192.168.0.8
- name: Get Subnet Info
@@ -36,19 +50,17 @@
- name: Verify Subnet Allocation Pools Exist
assert:
that:
- idem1 is not changed
- subnet_result.openstack_subnets is defined
- subnet_result.openstack_subnets | length == 1
- subnet_result.openstack_subnets[0].allocation_pools is defined
- subnet_result.openstack_subnets[0].allocation_pools | length == 2
- subnet_result.openstack_subnets[0].allocation_pools | length == 1
- name: Verify Subnet Allocation Pools
assert:
that:
- subnet_result.openstack_subnets[0].allocation_pools | selectattr('start','equalto',item.start) | list | count > 0
- subnet_result.openstack_subnets[0].allocation_pools | selectattr('end','equalto',item.end) | list | count > 0
loop:
- {start: '192.168.0.2', end: '192.168.0.4'}
- {start: '192.168.0.5', end: '192.168.0.8'}
- subnet_result.openstack_subnets[0].allocation_pools.0.start == '192.168.0.2'
- subnet_result.openstack_subnets[0].allocation_pools.0.end == '192.168.0.8'
- name: Delete subnet {{ subnet_name }}
openstack.cloud.subnet:

View File

@@ -21,6 +21,7 @@
cloud: "{{ cloud }}"
name: ansible_volume_snapshot
register: snap_info
ignore_errors: sdk_version is version('0.49', '<')
- name: Create volume backup
openstack.cloud.volume_backup:
@@ -29,12 +30,14 @@
display_name: ansible_volume_backup
volume: ansible_volume
register: vol_backup
ignore_errors: sdk_version is version('0.49', '<')
- name: Get backup info
openstack.cloud.volume_backup_info:
cloud: "{{ cloud }}"
name: ansible_volume_backup
register: backup_info
ignore_errors: sdk_version is version('0.49', '<')
- debug: var=vol
@@ -49,6 +52,7 @@
cloud: "{{ cloud }}"
display_name: ansible_volume_backup
state: absent
ignore_errors: sdk_version is version(0.49, '<')
- name: Delete volume snapshot
openstack.cloud.volume_snapshot:
@@ -62,3 +66,5 @@
cloud: "{{ cloud }}"
state: absent
display_name: ansible_volume
- include_tasks: volume_info.yml

View File

@@ -0,0 +1,155 @@
- name: Get info about volumes and all projects for all SDK
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: true
all_projects: true
register: all_sdk
ignore_errors: true
- name: Check info for all projects
assert:
that:
# Rocky SDK doesn't have all_projects attribute
- >-
(all_sdk is failed and sdk_version is version(0.19, '<')) or
all_sdk is success
- name: Get info about volumes for all SDK
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: true
register: all_sdk1
ignore_errors: true
- name: Check info for all SDK
assert:
that:
- all_sdk1 is success
- all_sdk1.volumes is defined
- name: Run tests for SDK > 0.28 (from train)
when: sdk_version is version(0.28, '>')
block:
- name: Get info about volumes
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
all_projects: true
register: delete
- name: Clean up volumes before the test
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: absent
display_name: "{{ vol.name }}"
loop: "{{ delete.volumes }}"
loop_control:
loop_var: vol
- name: Create volume
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: present
size: 1
display_name: ansible_test
display_description: testci
register: vol
- name: Get info about volumes
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: true
all_projects: true
register: info
- name: Check info
assert:
that:
- info.volumes | selectattr("description", "equalto", "testci") | list | length == 1
- info.volumes.0.name == 'ansible_test'
- info.volumes.0.status == 'available'
- name: Get not detailed info about volumes
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: false
all_projects: true
register: info1
- name: Check info
assert:
that:
- info1.volumes | selectattr("id", "equalto", "{{ info.volumes.0.id }}") | list | length == 1
- info1.volumes.0.name == 'ansible_test'
- info1.volumes.0.status == None
- name: Get info about volumes with name
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: false
name: ansible_test
all_projects: true
register: info2
- name: Check info
assert:
that:
- info2.volumes | length == 1
- info2.volumes.0.name == 'ansible_test'
- name: Get info about volumes with non-existent name
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: false
name: nothing_here
all_projects: true
register: info3
- name: Check info
assert:
that:
- info3.volumes | length == 0
- name: Get info about volumes
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: false
name: ansible_test
all_projects: true
register: info4
- name: Check info
assert:
that:
- info4.volumes | length == 1
- info4.volumes.0.name == 'ansible_test'
- name: Get info about volumes not from all projects
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
details: false
name: ansible_test
register: info4a
- name: Check info
assert:
that:
- info4a.volumes | length == 1
- info4a.volumes.0.name == 'ansible_test'
- name: Delete volume
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: absent
display_name: ansible_test
- name: Get info when no volumes
openstack.cloud.volume_info:
cloud: "{{ cloud }}"
all_projects: true
register: info5
- name: Check info
assert:
that:
- info5.volumes | selectattr("name", "equalto", "ansible_test") | list | length == 0

View File

@@ -106,6 +106,27 @@ then
exit 1
fi
# In case of Octavia enabled:
_octavia_image_path="/tmp/test-only-amphora-x64-haproxy-ubuntu-bionic.qcow2"
if systemctl list-units --full -all | grep -Fq "devstack@o-api.service" && \
test -f "$_octavia_image_path"
then
# Upload apmhora image for Octavia to test load balancers
OCTAVIA_AMP_IMAGE_FILE=${OCTAVIA_AMP_IMAGE_FILE:-"$_octavia_image_path"}
OCTAVIA_AMP_IMAGE_NAME=${OCTAVIA_AMP_IMAGE_NAME:-"test-only-amphora-x64-haproxy-ubuntu-bionic"}
OCTAVIA_AMP_IMAGE_SIZE=${OCTAVIA_AMP_IMAGE_SIZE:-3}
openstack --os-cloud=${CLOUD} image create \
--container-format bare \
--disk-format qcow2 \
--private \
--file $OCTAVIA_AMP_IMAGE_FILE \
--project service $OCTAVIA_AMP_IMAGE_NAME
openstack --os-cloud=${CLOUD} image set --tag amphora $OCTAVIA_AMP_IMAGE_NAME
# End of Octavia preparement
else
tag_opt="$tag_opt --skip-tags loadbalancer"
fi
# Discover openstackSDK version
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
pushd ci/

View File

@@ -4,12 +4,19 @@
gather_facts: true
roles:
- { role: address_scope, tags: address_scope }
- { role: auth, tags: auth }
- { role: client_config, tags: client_config }
- { role: dns_zone_info, tags: dns_zone_info }
- role: object_container
tags: object_container
when: sdk_version is version(0.18, '>=')
- { role: group, tags: group }
# TODO(mordred) Reenable this once the fixed openstack.cloud.image winds up in an
# upstream ansible release.
# - { role: image, tags: image }
- role: dns
tags: dns
when: sdk_version is version(0.28, '>=')
- { role: floating_ip_info, tags: floating_ip_info }
- { role: image, tags: image }
- { role: keypair, tags: keypair }
- { role: keystone_domain, tags: keystone_domain }
- role: keystone_mapping
@@ -38,3 +45,8 @@
- { role: user_group, tags: user_group }
- { role: user_role, tags: user_role }
- { role: volume, tags: volume }
- role: orchestration
tags: orchestrate
when: sdk_version is version("0.53.0", '>=')
- role: loadbalancer
tags: loadbalancer

View File

@@ -58,10 +58,10 @@ Libraries
users as a primary audience, they are for intra-server communication.
* All modules should be registered in ``meta/action_groups.yml`` for enabling the
variables to be set in `group level
<https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html>`.
<https://docs.ansible.com/ansible/latest/user_guide/playbooks_module_defaults.html>`_.
Testing
-------
* Integration testing is currently done in `OpenStack's CI system
<https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/zuul.yaml>`
<https://opendev.org/openstack/ansible-collections-openstack/src/branch/master/.zuul.yaml>`_

36
galaxy.yml Normal file
View File

@@ -0,0 +1,36 @@
namespace: openstack
name: cloud
readme: README.md
authors: Openstack
description: Openstack Ansible modules
license: GPL-3.0-or-later
tags:
- cloud
- openstack
dependencies: {}
repository: https://opendev.org/openstack/ansible-collections-openstack
documentation: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html
homepage: https://opendev.org/openstack/ansible-collections-openstack
issues: https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack
build_ignore:
- "*.tar.gz"
- build_artifact
- ci
- galaxy.yml.in
- setup.cfg
- test-requirements*
- tests
- tools
- tox.ini
- .gitignore
- .gitreview
- .zuul.yaml
- .pytest_cache
- importer_result.json
- .tox
- .env
- .vscode
- ansible_collections_openstack.egg-info
- contrib
- changelogs
version: 1.5.1-dev

View File

@@ -8,10 +8,10 @@ tags:
- cloud
- openstack
dependencies: {}
repository: https://opendev.org/openstack/ansible-collections-openstack.git
documentation: https://docs.openstack.org/ansible-collections-openstack
homepage: https://opendev.org
issues: https://review.opendev.org/q/project:openstack/ansible-collections-openstack
repository: https://opendev.org/openstack/ansible-collections-openstack
documentation: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/index.html
homepage: https://opendev.org/openstack/ansible-collections-openstack
issues: https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack
build_ignore:
- "*.tar.gz"
- build_artifact
@@ -27,3 +27,9 @@ build_ignore:
- .zuul.yaml
- .pytest_cache
- importer_result.json
- .tox
- .env
- ansible_collections_openstack.egg-info
- contrib
- changelogs/.plugin-cache.yaml
- changelogs/fragments

View File

@@ -1,5 +1,7 @@
requires_ansible: ">=2.8"
action_groups:
openstack:
- address_scope
- auth
- baremetal_inspect
- baremetal_inspect
@@ -20,7 +22,7 @@ action_groups:
- config
- config
- dns_zone
- dns_zone
- dns_zone_info
- endpoint
- endpoint
- federation_idp
@@ -32,6 +34,7 @@ action_groups:
- federation_mapping_info
- federation_mapping_info
- floating_ip
- floating_ip_info
- group_assignment
- group_assignment
- host_aggregate
@@ -53,6 +56,7 @@ action_groups:
- image
- image_info
- keypair
- keypair_info
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
@@ -65,6 +69,7 @@ action_groups:
- network
- networks_info
- object
- object_container
- port
- port_info
- project
@@ -77,7 +82,9 @@ action_groups:
- router
- routers_info
- security_group
- security_group_info
- security_group_rule
- security_group_rule_info
- server
- server_action
- server_group
@@ -147,6 +154,7 @@ action_groups:
- image
- image_info
- keypair
- keypair_info
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
@@ -159,6 +167,7 @@ action_groups:
- network
- networks_info
- object
- object_container
- port
- port_info
- project
@@ -171,7 +180,9 @@ action_groups:
- router
- routers_info
- security_group
- security_group_info
- security_group_rule
- security_group_rule_info
- server
- server_action
- server_group

View File

@@ -61,7 +61,6 @@ options:
- Whether or not SSL API requests should be verified.
- Before Ansible 2.3 this defaulted to C(yes).
type: bool
default: no
aliases: [ verify ]
ca_cert:
description:

View File

@@ -22,7 +22,7 @@ options:
plugin:
description: token that ensures this is a source file for the 'openstack' plugin.
required: True
choices: ['openstack']
choices: ['openstack', 'openstack.cloud.openstack']
show_all:
description: toggles showing all vms vs only those with a working IP
type: bool
@@ -98,6 +98,10 @@ options:
description: Add hosts to group based on Jinja2 conditionals.
type: dictionary
default: {}
legacy_groups:
description: Automatically create groups from host variables.
type: bool
default: true
extends_documentation_fragment:
- inventory_cache
@@ -108,7 +112,7 @@ extends_documentation_fragment:
EXAMPLES = '''
# file must be named openstack.yaml or openstack.yml
# Make the plugin behave like the default behavior of the old script
plugin: openstack
plugin: openstack.cloud.openstack
expand_hostvars: yes
fail_on_errors: yes
all_projects: yes
@@ -155,7 +159,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
msg = ''
if not self._config_data:
msg = 'File empty. this is not my config file'
elif 'plugin' in self._config_data and self._config_data['plugin'] != self.NAME:
elif 'plugin' in self._config_data and self._config_data['plugin'] not in (self.NAME, 'openstack'):
msg = 'plugin config file, but not for us: %s' % self._config_data['plugin']
elif 'plugin' not in self._config_data and 'clouds' not in self._config_data:
msg = "it's not a plugin configuration nor a clouds.yaml file"
@@ -361,8 +365,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
openstack=server)
self.inventory.add_host(current_host)
for group in self._get_groups_from_server(server, namegroup=namegroup):
groups[group].append(current_host)
if self.get_option('legacy_groups'):
for group in self._get_groups_from_server(server, namegroup=namegroup):
groups[group].append(current_host)
def verify_file(self, path):

View File

@@ -0,0 +1,68 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
# still belong to the author of the module, and may assign their own license
# to the complete work.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
def ironic_argument_spec(**kwargs):
spec = dict(
auth_type=dict(required=False),
ironic_url=dict(required=False),
)
spec.update(kwargs)
return openstack_full_argument_spec(**spec)
# TODO(dtantsur): inherit the collection's base module
class IronicModule(AnsibleModule):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._update_ironic_auth()
def _update_ironic_auth(self):
"""Validate and update authentication parameters for ironic."""
if (
self.params['auth_type'] in [None, 'None', 'none']
and self.params['ironic_url'] is None
and not self.params['cloud']
and not (self.params['auth']
and self.params['auth'].get('endpoint'))
):
self.fail_json(msg=("Authentication appears to be disabled, "
"Please define either ironic_url, or cloud, "
"or auth.endpoint"))
if (
self.params['ironic_url']
and self.params['auth_type'] in [None, 'None', 'none']
and not (self.params['auth']
and self.params['auth'].get('endpoint'))
):
self.params['auth'] = dict(
endpoint=self.params['ironic_url']
)

View File

@@ -67,6 +67,8 @@ OVERRIDES = {'os_client_config': 'config',
CUSTOM_VAR_PARAMS = ['min_ver', 'max_ver']
MINIMUM_SDK_VERSION = '0.12.0'
def openstack_argument_spec():
# DEPRECATED: This argument spec is only used for the deprecated old
@@ -150,7 +152,7 @@ def openstack_module_kwargs(**kwargs):
# for compatibility with old versions
def openstack_cloud_from_module(module, min_version='0.12.0'):
def openstack_cloud_from_module(module, min_version=None):
try:
# Due to the name shadowing we should import other way
sdk = importlib.import_module('openstack')
@@ -159,9 +161,10 @@ def openstack_cloud_from_module(module, min_version='0.12.0'):
module.fail_json(msg='openstacksdk is required for this module')
if min_version:
min_version = max(StrictVersion('0.12.0'), StrictVersion(min_version))
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(min_version))
else:
min_version = StrictVersion('0.12.0')
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(sdk_version.__version__) < min_version:
module.fail_json(
@@ -240,6 +243,7 @@ class OpenStackModule:
deprecated_names = ()
argument_spec = {}
module_kwargs = {}
module_min_sdk_version = None
def __init__(self):
"""Initialize Openstack base class.
@@ -252,6 +256,7 @@ class OpenStackModule:
**self.module_kwargs)
self.params = self.ansible.params
self.module_name = self.ansible._name
self.check_mode = self.ansible.check_mode
self.sdk_version = None
self.results = {'changed': False}
self.exit = self.exit_json = self.ansible.exit_json
@@ -286,7 +291,7 @@ class OpenStackModule:
"The '%s' module has been renamed to '%s' in openstack "
"collection: openstack.cloud.%s" % (
self.module_name, new_module_name, new_module_name),
version='2.10')
version='2.0.0', collection_name='openstack.cloud')
def openstack_cloud_from_module(self):
"""Sets up connection to cloud using provided options. Checks if all
@@ -300,6 +305,19 @@ class OpenStackModule:
except ImportError:
self.fail_json(msg='openstacksdk is required for this module')
# Fail if the available SDK version doesn't meet the minimum version
# requirements
if self.module_min_sdk_version:
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(self.module_min_sdk_version))
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(self.sdk_version) < min_version:
self.fail(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=min_version))
# Fail if there are set unsupported for this version parameters
# New parameters should NOT use 'default' but rely on SDK defaults
for param in self.argument_spec:

View File

@@ -0,0 +1,201 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2021 by Uemit Seren <uemit.seren@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: address_scope
short_description: Create or delete address scopes from OpenStack
author: OpenStack Ansible SIG
description:
- Create or Delete address scopes from OpenStack.
options:
state:
description:
- Indicate desired state of the resource
choices: ['present', 'absent']
default: present
type: str
name:
description:
- Name to be give to the address scope
required: true
type: str
project:
description:
- Unique name or ID of the project.
type: str
ip_version:
description:
- The IP version of the subnet 4 or 6
default: 4
type: str
choices: ['4', '6']
shared:
description:
- Whether this address scope is shared or not.
type: bool
default: 'no'
extra_specs:
description:
- Dictionary with extra key/value pairs passed to the API
required: false
default: {}
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create an IPv4 address scope.
- openstack.cloud.address_scope:
cloud: mycloud
state: present
name: my_adress_scope
# Create a shared IPv6 address scope for a given project.
- openstack.cloud.address_scope:
cloud: mycloud
state: present
ip_version: 6
name: ipv6_address_scope
project: myproj
# Delete address scope.
- openstack.cloud.address_scope:
cloud: mycloud
state: absent
name: my_adress_scope
'''
RETURN = '''
address_scope:
description: Dictionary describing the address scope.
returned: On success when I(state) is 'present'
type: complex
contains:
id:
description: Address Scope ID.
type: str
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
name:
description: Address Scope name.
type: str
sample: "my_address_scope"
tenant_id:
description: The tenant ID.
type: str
sample: "861174b82b43463c9edc5202aadc60ef"
ip_version:
description: The IP version of the subnet 4 or 6.
type: str
sample: "4"
is_shared:
description: Indicates whether this address scope is shared across all tenants.
type: bool
sample: false
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class AddressScopeModule(OpenStackModule):
argument_spec = dict(
state=dict(default='present', choices=['absent', 'present']),
name=dict(required=True),
shared=dict(default=False, type='bool'),
ip_version=dict(type='str', default='4', choices=['4', '6']),
project=dict(default=None),
extra_specs=dict(type='dict', default=dict())
)
def _needs_update(self, address_scope, filters=None):
"""Decide if the given address_scope needs an update.
"""
ip_version = int(self.params['ip_version'])
if address_scope['is_shared'] != self.params['shared']:
return True
if ip_version and address_scope['ip_version'] != ip_version:
self.fail_json(msg='Cannot update ip_version in existing address scope')
return False
def _system_state_change(self, address_scope, filters=None):
"""Check if the system state would be changed."""
state = self.params['state']
if state == 'absent' and address_scope:
return True
if state == 'present':
if not address_scope:
return True
return self._needs_update(address_scope, filters)
return False
def run(self):
state = self.params['state']
name = self.params['name']
shared = self.params['shared']
ip_version = self.params['ip_version']
project = self.params['project']
extra_specs = self.params['extra_specs']
if project is not None:
proj = self.conn.get_project(project)
if proj is None:
self.fail(msg='Project %s could not be found' % project)
project_id = proj['id']
else:
project_id = self.conn.current_project_id
address_scope = self.conn.network.find_address_scope(name_or_id=name)
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(address_scope)
)
if state == 'present':
changed = False
if not address_scope:
kwargs = dict(
name=name,
ip_version=ip_version,
is_shared=shared,
tenant_id=project_id)
dup_args = set(kwargs.keys()) & set(extra_specs.keys())
if dup_args:
raise ValueError('Duplicate key(s) {0} in extra_specs'
.format(list(dup_args)))
kwargs = dict(kwargs, **extra_specs)
address_scope = self.conn.network.create_address_scope(**kwargs)
changed = True
else:
if self._needs_update(address_scope):
address_scope = self.conn.network.update_address_scope(address_scope['id'], is_shared=shared)
changed = True
else:
changed = False
self.exit_json(changed=changed, address_scope=address_scope, id=address_scope['id'])
elif state == 'absent':
if not address_scope:
self.exit(changed=False)
else:
self.conn.network.delete_address_scope(address_scope['id'])
self.exit_json(changed=True)
def main():
module = AddressScopeModule()
module()
if __name__ == '__main__':
main()

View File

@@ -38,29 +38,24 @@ service_catalog:
type: dict
'''
import traceback
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
class AuthModule(OpenStackModule):
argument_spec = dict()
module_kwargs = dict()
def run(self):
self.exit_json(
changed=False,
ansible_facts=dict(
auth_token=self.conn.auth_token,
service_catalog=self.conn.service_catalog))
def main():
argument_spec = openstack_full_argument_spec()
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
module.exit_json(
changed=False,
ansible_facts=dict(
auth_token=cloud.auth_token,
service_catalog=cloud.service_catalog))
except Exception as e:
module.fail_json(msg=str(e), exception=traceback.format_exc())
module = AuthModule()
module()
if __name__ == '__main__':

View File

@@ -75,10 +75,14 @@ EXAMPLES = '''
name: "testnode1"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
def _choose_id_value(module):
@@ -90,37 +94,14 @@ def _choose_id_value(module):
def main():
argument_spec = openstack_full_argument_spec(
auth_type=dict(required=False),
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
mac=dict(required=False),
ironic_url=dict(required=False),
timeout=dict(default=1200, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None', 'none']
and module.params['ironic_url'] is None
and not module.params['cloud']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define either ironic_url, or cloud, "
"or auth.endpoint")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None', 'none']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
module = IronicModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:

View File

@@ -43,29 +43,71 @@ options:
endpoint URL for the Ironic API. Use with "auth" and "auth_type"
settings set to None.
type: str
resource_class:
description:
- The specific resource type to which this node belongs.
type: str
bios_interface:
description:
- The bios interface for this node, e.g. "no-bios".
type: str
boot_interface:
description:
- The boot interface for this node, e.g. "pxe".
type: str
console_interface:
description:
- The console interface for this node, e.g. "no-console".
type: str
deploy_interface:
description:
- The deploy interface for this node, e.g. "iscsi".
type: str
inspect_interface:
description:
- The interface used for node inspection, e.g. "no-inspect".
type: str
management_interface:
description:
- The interface for out-of-band management of this node, e.g.
"ipmitool".
type: str
network_interface:
description:
- The network interface provider to use when describing
connections for this node.
type: str
power_interface:
description:
- The interface used to manage power actions on this node, e.g.
"ipmitool".
type: str
raid_interface:
description:
- Interface used for configuring raid on this node.
type: str
rescue_interface:
description:
- Interface used for node rescue, e.g. "no-rescue".
type: str
storage_interface:
description:
- Interface used for attaching and detaching volumes on this node, e.g.
"cinder".
type: str
vendor_interface:
description:
- Interface for all vendor-specific actions on this node, e.g.
"no-vendor".
type: str
driver_info:
description:
- Information for this server's driver. Will vary based on which
driver is in use. Any sub-field which is populated will be validated
during creation.
during creation. For compatibility reasons sub-fields `power`,
`deploy`, `management` and `console` are flattened.
required: true
type: dict
suboptions:
power:
description:
- Information necessary to turn this server on / off.
This often includes such things as IPMI username, password, and IP address.
required: true
deploy:
description:
- Information necessary to deploy this server directly, without using Nova. THIS IS NOT RECOMMENDED.
console:
description:
- Information necessary to connect to this server's serial console. Not all drivers support this.
management:
description:
- Information necessary to interact with this server's management interface. May be shared by power_info in some cases.
required: true
nics:
description:
- 'A list of network interface cards, eg, " - mac: aa:bb:cc:aa:bb:cc"'
@@ -120,7 +162,6 @@ options:
re-assert the password field.
- C(skip_update_of_driver_password) is deprecated alias and will be removed in openstack.cloud 2.0.0.
type: bool
default: 'no'
aliases:
- skip_update_of_driver_password
requirements:
@@ -150,10 +191,9 @@ EXAMPLES = '''
- mac: "aa:bb:cc:aa:bb:cc"
- mac: "dd:ee:ff:dd:ee:ff"
driver_info:
power:
ipmi_address: "1.2.3.4"
ipmi_username: "admin"
ipmi_password: "adminpass"
ipmi_address: "1.2.3.4"
ipmi_username: "admin"
ipmi_password: "adminpass"
chassis_uuid: "00000000-0000-0000-0000-000000000001"
'''
@@ -164,37 +204,46 @@ try:
except ImportError:
HAS_JSONPATCH = False
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
_PROPERTIES = {
'cpu_arch': 'cpu_arch',
'cpus': 'cpus',
'ram': 'memory_mb',
'disk_size': 'local_gb',
'capabilities': 'capabilities',
'root_device': 'root_device',
}
def _parse_properties(module):
"""Convert ansible properties into native ironic values.
Also filter out any properties that are not set.
"""
p = module.params['properties']
props = dict(
cpu_arch=p.get('cpu_arch') if p.get('cpu_arch') else 'x86_64',
cpus=p.get('cpus') if p.get('cpus') else 1,
memory_mb=p.get('ram') if p.get('ram') else 1,
local_gb=p.get('disk_size') if p.get('disk_size') else 1,
capabilities=p.get('capabilities') if p.get('capabilities') else '',
root_device=p.get('root_device') if p.get('root_device') else '',
)
return props
return {to_key: p[from_key] for (from_key, to_key) in _PROPERTIES.items()
if p.get(from_key) is not None}
def _parse_driver_info(sdk, module):
p = module.params['driver_info']
info = p.get('power')
if not info:
raise sdk.exceptions.OpenStackCloudException(
"driver_info['power'] is required")
if p.get('console'):
info.update(p.get('console'))
if p.get('management'):
info.update(p.get('management'))
if p.get('deploy'):
info.update(p.get('deploy'))
info = module.params['driver_info'].copy()
for deprecated in ('power', 'console', 'management', 'deploy'):
if deprecated in info:
info.update(info.pop(deprecated))
module.deprecate("Suboption %s of the driver_info parameter of "
"'openstack.cloud.baremetal_node' is deprecated"
% deprecated, version='2.0.0',
collection_name='openstack.cloud')
return info
@@ -225,14 +274,26 @@ def _exit_node_not_updated(module, server):
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
driver=dict(required=False),
resource_class=dict(required=False),
bios_interface=dict(required=False),
boot_interface=dict(required=False),
console_interface=dict(required=False),
deploy_interface=dict(required=False),
inspect_interface=dict(required=False),
management_interface=dict(required=False),
network_interface=dict(required=False),
power_interface=dict(required=False),
raid_interface=dict(required=False),
rescue_interface=dict(required=False),
storage_interface=dict(required=False),
vendor_interface=dict(required=False),
driver_info=dict(type='dict', required=True),
nics=dict(type='list', required=True, elements="dict"),
properties=dict(type='dict', default={}),
ironic_url=dict(required=False),
chassis_uuid=dict(required=False),
skip_update_of_masked_password=dict(
required=False,
@@ -243,30 +304,10 @@ def main():
state=dict(required=False, default='present', choices=['present', 'absent'])
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
module = IronicModule(argument_spec, **module_kwargs)
if not HAS_JSONPATCH:
module.fail_json(msg='jsonpatch is required for this module')
if (
module.params['auth_type'] in [None, 'None', 'none']
and module.params['ironic_url'] is None
and not module.params['cloud']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define either ironic_url, or cloud, "
"or auth.endpoint")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None', 'none']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
node_id = _choose_id_value(module)
@@ -286,6 +327,22 @@ def main():
driver_info=driver_info,
name=module.params['name'],
)
optional_field_names = ('resource_class',
'bios_interface',
'boot_interface',
'console_interface',
'deploy_interface',
'inspect_interface',
'management_interface',
'network_interface',
'power_interface',
'raid_interface',
'rescue_interface',
'storage_interface',
'vendor_interface')
for i in optional_field_names:
if module.params[i]:
kwargs[i] = module.params[i]
if module.params['chassis_uuid']:
kwargs['chassis_uuid'] = module.params['chassis_uuid']

View File

@@ -132,10 +132,15 @@ EXAMPLES = '''
delegate_to: localhost
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
def _choose_id_value(module):
@@ -227,12 +232,11 @@ def _check_set_power_state(module, cloud, node):
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
instance_info=dict(type='dict', required=False),
config_drive=dict(type='raw', required=False),
ironic_url=dict(required=False),
state=dict(required=False, default='present'),
maintenance=dict(required=False),
maintenance_reason=dict(required=False),
@@ -242,28 +246,7 @@ def main():
timeout=dict(required=False, type='int', default=1800),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None', 'none']
and module.params['ironic_url'] is None
and not module.params['cloud']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define either ironic_url, or cloud, "
"or auth.endpoint")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None', 'none']
and not (module.params['auth']
and module.params['auth'].get('endpoint'))
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
module = IronicModule(argument_spec, **module_kwargs)
if (
module.params['config_drive']

View File

@@ -94,36 +94,11 @@ id:
sample: "3292f020780b4d5baf27ff7e1d224c44"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, service):
if service.enabled != module.params['enabled']:
return True
if service.description is not None and \
service.description != module.params['description']:
return True
return False
def _system_state_change(module, service):
state = module.params['state']
if state == 'absent' and service:
return True
if state == 'present':
if service is None:
return True
return _needs_update(module, service)
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityCatalogServiceModule(OpenStackModule):
argument_spec = dict(
description=dict(default=None),
enabled=dict(default=True, type='bool'),
name=dict(required=True),
@@ -131,58 +106,79 @@ def main():
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
description = module.params['description']
enabled = module.params['enabled']
name = module.params['name']
state = module.params['state']
service_type = module.params['service_type']
def _needs_update(self, service):
if service.enabled != self.params['enabled']:
return True
if service.description is not None and \
service.description != self.params['description']:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
services = cloud.search_services(name_or_id=name,
filters=dict(type=service_type))
def _system_state_change(self, service):
state = self.params['state']
if state == 'absent' and service:
return True
if state == 'present':
if service is None:
return True
return self._needs_update(service)
return False
def run(self):
description = self.params['description']
enabled = self.params['enabled']
name = self.params['name']
state = self.params['state']
service_type = self.params['service_type']
services = self.conn.search_services(
name_or_id=name, filters=dict(type=service_type))
if len(services) > 1:
module.fail_json(msg='Service name %s and type %s are not unique' %
(name, service_type))
self.fail_json(
msg='Service name %s and type %s are not unique'
% (name, service_type))
elif len(services) == 1:
service = services[0]
else:
service = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, service))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(service))
if state == 'present':
if service is None:
service = cloud.create_service(name=name, description=description,
type=service_type, enabled=True)
service = self.conn.create_service(
name=name, description=description, type=service_type, enabled=True)
changed = True
else:
if _needs_update(module, service):
service = cloud.update_service(
if self._needs_update(service):
service = self.conn.update_service(
service.id, name=name, type=service_type, enabled=enabled,
description=description)
changed = True
else:
changed = False
module.exit_json(changed=changed, service=service, id=service.id)
self.exit_json(changed=changed, service=service, id=service.id)
elif state == 'absent':
if service is None:
changed = False
else:
cloud.delete_service(service.id)
self.conn.delete_service(service.id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityCatalogServiceModule()
module()
if __name__ == '__main__':

View File

@@ -206,31 +206,16 @@ EXAMPLES = '''
node_count: 5
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
class CoeClusterModule(OpenStackModule):
argument_spec = dict(
cluster_template_id=dict(required=True),
discovery_url=dict(default=None),
docker_volume_size=dict(type='int'),
flavor_id=dict(default=None),
keypair=dict(default=None),
keypair=dict(default=None, no_log=False),
labels=dict(default=None, type='raw'),
master_count=dict(type='int', default=1),
master_flavor_id=dict(default=None),
@@ -239,35 +224,46 @@ def main():
state=dict(default='present', choices=['absent', 'present']),
timeout=dict(type='int', default=60),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
module_kwargs = dict()
params = module.params.copy()
def _parse_labels(self, labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
state = module.params['state']
name = module.params['name']
cluster_template_id = module.params['cluster_template_id']
def run(self):
params = self.params.copy()
kwargs = dict(
discovery_url=module.params['discovery_url'],
docker_volume_size=module.params['docker_volume_size'],
flavor_id=module.params['flavor_id'],
keypair=module.params['keypair'],
labels=_parse_labels(params['labels']),
master_count=module.params['master_count'],
master_flavor_id=module.params['master_flavor_id'],
node_count=module.params['node_count'],
create_timeout=module.params['timeout'],
)
state = self.params['state']
name = self.params['name']
cluster_template_id = self.params['cluster_template_id']
kwargs = dict(
discovery_url=self.params['discovery_url'],
docker_volume_size=self.params['docker_volume_size'],
flavor_id=self.params['flavor_id'],
keypair=self.params['keypair'],
labels=self._parse_labels(params['labels']),
master_count=self.params['master_count'],
master_flavor_id=self.params['master_flavor_id'],
node_count=self.params['node_count'],
create_timeout=self.params['timeout'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
cluster = cloud.get_coe_cluster(name_or_id=name, filters={'cluster_template_id': cluster_template_id})
cluster = self.conn.get_coe_cluster(
name_or_id=name, filters={'cluster_template_id': cluster_template_id})
if state == 'present':
if not cluster:
cluster = cloud.create_coe_cluster(name, cluster_template_id=cluster_template_id, **kwargs)
cluster = self.conn.create_coe_cluster(
name, cluster_template_id=cluster_template_id, **kwargs)
changed = True
else:
changed = False
@@ -278,15 +274,18 @@ def main():
# therefore try `id` first then `uuid`.
cluster_id = cluster.get('id', cluster.get('uuid'))
cluster['id'] = cluster['uuid'] = cluster_id
module.exit_json(changed=changed, cluster=cluster, id=cluster_id)
self.exit_json(changed=changed, cluster=cluster, id=cluster_id)
elif state == 'absent':
if not cluster:
module.exit_json(changed=False)
self.exit_json(changed=False)
else:
cloud.delete_coe_cluster(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.conn.delete_coe_cluster(name)
self.exit_json(changed=True)
def main():
module = CoeClusterModule()
module()
if __name__ == "__main__":

View File

@@ -281,26 +281,11 @@ EXAMPLES = '''
public: no
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _parse_labels(labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
def main():
argument_spec = openstack_full_argument_spec(
class CoeClusterTemplateModule(OpenStackModule):
argument_spec = dict(
coe=dict(required=True, choices=['kubernetes', 'swarm', 'mesos']),
dns_nameserver=dict(default='8.8.8.8'),
docker_storage_driver=dict(choices=['devicemapper', 'overlay', 'overlay2']),
@@ -327,61 +312,76 @@ def main():
tls_disabled=dict(type='bool', default=False),
volume_driver=dict(choices=['cinder', 'rexray']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
module_kwargs = dict()
params = module.params.copy()
def _parse_labels(self, labels):
if isinstance(labels, str):
labels_dict = {}
for kv_str in labels.split(","):
k, v = kv_str.split("=")
labels_dict[k] = v
return labels_dict
if not labels:
return {}
return labels
state = module.params['state']
name = module.params['name']
coe = module.params['coe']
image_id = module.params['image_id']
def run(self):
params = self.params.copy()
kwargs = dict(
dns_nameserver=module.params['dns_nameserver'],
docker_storage_driver=module.params['docker_storage_driver'],
docker_volume_size=module.params['docker_volume_size'],
external_network_id=module.params['external_network_id'],
fixed_network=module.params['fixed_network'],
fixed_subnet=module.params['fixed_subnet'],
flavor_id=module.params['flavor_id'],
floating_ip_enabled=module.params['floating_ip_enabled'],
keypair_id=module.params['keypair_id'],
labels=_parse_labels(params['labels']),
http_proxy=module.params['http_proxy'],
https_proxy=module.params['https_proxy'],
master_lb_enabled=module.params['master_lb_enabled'],
master_flavor_id=module.params['master_flavor_id'],
network_driver=module.params['network_driver'],
no_proxy=module.params['no_proxy'],
public=module.params['public'],
registry_enabled=module.params['registry_enabled'],
server_type=module.params['server_type'],
tls_disabled=module.params['tls_disabled'],
volume_driver=module.params['volume_driver'],
)
state = self.params['state']
name = self.params['name']
coe = self.params['coe']
image_id = self.params['image_id']
kwargs = dict(
dns_nameserver=self.params['dns_nameserver'],
docker_storage_driver=self.params['docker_storage_driver'],
docker_volume_size=self.params['docker_volume_size'],
external_network_id=self.params['external_network_id'],
fixed_network=self.params['fixed_network'],
fixed_subnet=self.params['fixed_subnet'],
flavor_id=self.params['flavor_id'],
floating_ip_enabled=self.params['floating_ip_enabled'],
keypair_id=self.params['keypair_id'],
labels=self._parse_labels(params['labels']),
http_proxy=self.params['http_proxy'],
https_proxy=self.params['https_proxy'],
master_lb_enabled=self.params['master_lb_enabled'],
master_flavor_id=self.params['master_flavor_id'],
network_driver=self.params['network_driver'],
no_proxy=self.params['no_proxy'],
public=self.params['public'],
registry_enabled=self.params['registry_enabled'],
server_type=self.params['server_type'],
tls_disabled=self.params['tls_disabled'],
volume_driver=self.params['volume_driver'],
)
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = False
template = cloud.get_coe_cluster_template(name_or_id=name, filters={'coe': coe, 'image_id': image_id})
template = self.conn.get_coe_cluster_template(
name_or_id=name, filters={'coe': coe, 'image_id': image_id})
if state == 'present':
if not template:
template = cloud.create_coe_cluster_template(name, coe=coe, image_id=image_id, **kwargs)
template = self.conn.create_coe_cluster_template(
name, coe=coe, image_id=image_id, **kwargs)
changed = True
else:
changed = False
module.exit_json(changed=changed, cluster_template=template, id=template['uuid'])
self.exit_json(
changed=changed, cluster_template=template, id=template['uuid'])
elif state == 'absent':
if not template:
module.exit_json(changed=False)
self.exit_json(changed=False)
else:
cloud.delete_coe_cluster_template(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.conn.delete_coe_cluster_template(name)
self.exit_json(changed=True)
def main():
module = CoeClusterTemplateModule()
module()
if __name__ == "__main__":

View File

@@ -160,23 +160,11 @@ flavor:
"aggregate_instance_extra_specs:pinned": false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(module, flavor):
state = module.params['state']
if state == 'present' and not flavor:
return True
if state == 'absent' and flavor:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class ComputeFlavorModule(OpenStackModule):
argument_spec = dict(
state=dict(required=False, default='present',
choices=['absent', 'present']),
name=dict(required=True),
@@ -194,25 +182,30 @@ def main():
extra_specs=dict(required=False, default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
module_kwargs = dict(
required_if=[
('state', 'present', ['ram', 'vcpus', 'disk'])
],
**module_kwargs)
supports_check_mode=True
)
state = module.params['state']
name = module.params['name']
extra_specs = module.params['extra_specs'] or {}
def _system_state_change(self, flavor):
state = self.params['state']
if state == 'present' and not flavor:
return True
if state == 'absent' and flavor:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
flavor = cloud.get_flavor(name)
def run(self):
state = self.params['state']
name = self.params['name']
extra_specs = self.params['extra_specs'] or {}
if module.check_mode:
module.exit_json(changed=_system_state_change(module, flavor))
flavor = self.conn.get_flavor(name)
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(flavor))
if state == 'present':
old_extra_specs = {}
@@ -220,26 +213,29 @@ def main():
if flavor:
old_extra_specs = flavor['extra_specs']
for param_key in ['ram', 'vcpus', 'disk', 'ephemeral', 'swap', 'rxtx_factor', 'is_public']:
if module.params[param_key] != flavor[param_key]:
if flavor['swap'] == "":
flavor['swap'] = 0
for param_key in ['ram', 'vcpus', 'disk', 'ephemeral',
'swap', 'rxtx_factor', 'is_public']:
if self.params[param_key] != flavor[param_key]:
require_update = True
break
if flavor and require_update:
cloud.delete_flavor(name)
self.conn.delete_flavor(name)
flavor = None
if not flavor:
flavor = cloud.create_flavor(
flavor = self.conn.create_flavor(
name=name,
ram=module.params['ram'],
vcpus=module.params['vcpus'],
disk=module.params['disk'],
flavorid=module.params['flavorid'],
ephemeral=module.params['ephemeral'],
swap=module.params['swap'],
rxtx_factor=module.params['rxtx_factor'],
is_public=module.params['is_public']
ram=self.params['ram'],
vcpus=self.params['vcpus'],
disk=self.params['disk'],
flavorid=self.params['flavorid'],
ephemeral=self.params['ephemeral'],
swap=self.params['swap'],
rxtx_factor=self.params['rxtx_factor'],
is_public=self.params['is_public']
)
changed = True
else:
@@ -249,25 +245,26 @@ def main():
unset_keys = set(old_extra_specs.keys()) - set(extra_specs.keys())
if unset_keys and not require_update:
cloud.unset_flavor_specs(flavor['id'], unset_keys)
self.conn.unset_flavor_specs(flavor['id'], unset_keys)
if old_extra_specs != new_extra_specs:
cloud.set_flavor_specs(flavor['id'], extra_specs)
self.conn.set_flavor_specs(flavor['id'], extra_specs)
changed = (changed or old_extra_specs != new_extra_specs)
module.exit_json(changed=changed,
flavor=flavor,
id=flavor['id'])
self.exit_json(
changed=changed, flavor=flavor, id=flavor['id'])
elif state == 'absent':
if flavor:
cloud.delete_flavor(name)
module.exit_json(changed=True)
module.exit_json(changed=False)
self.conn.delete_flavor(name)
self.exit_json(changed=True)
self.exit_json(changed=False)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = ComputeFlavorModule()
module()
if __name__ == '__main__':

View File

@@ -158,70 +158,59 @@ openstack_flavors:
sample: true
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class ComputeFlavorInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, default=None),
ram=dict(required=False, default=None),
vcpus=dict(required=False, default=None),
limit=dict(required=False, default=None, type='int'),
ephemeral=dict(required=False, default=None),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[
['name', 'ram'],
['name', 'vcpus'],
['name', 'ephemeral']
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.compute_flavor_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.compute_flavor_facts' module has been renamed to 'openstack.cloud.compute_flavor_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
name = module.params['name']
vcpus = module.params['vcpus']
ram = module.params['ram']
ephemeral = module.params['ephemeral']
limit = module.params['limit']
deprecated_names = ('openstack.cloud.compute_flavor_facts')
filters = {}
if vcpus:
filters['vcpus'] = vcpus
if ram:
filters['ram'] = ram
if ephemeral:
filters['ephemeral'] = ephemeral
def run(self):
name = self.params['name']
vcpus = self.params['vcpus']
ram = self.params['ram']
ephemeral = self.params['ephemeral']
limit = self.params['limit']
filters = {}
if vcpus:
filters['vcpus'] = vcpus
if ram:
filters['ram'] = ram
if ephemeral:
filters['ephemeral'] = ephemeral
sdk, cloud = openstack_cloud_from_module(module)
try:
if name:
flavors = cloud.search_flavors(filters={'name': name})
flavors = self.conn.search_flavors(filters={'name': name})
else:
flavors = cloud.list_flavors()
flavors = self.conn.list_flavors()
if filters:
flavors = cloud.range_search(flavors, filters)
flavors = self.conn.range_search(flavors, filters)
if limit is not None:
flavors = flavors[:limit]
if is_old_facts:
module.exit_json(changed=False,
ansible_facts=dict(openstack_flavors=flavors))
else:
module.exit_json(changed=False,
openstack_flavors=flavors)
self.exit_json(changed=False, openstack_flavors=flavors)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = ComputeFlavorInfoModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1 @@
object_container.py

View File

@@ -114,128 +114,130 @@ zone:
sample: []
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, email, description, ttl, masters, zone):
if state == 'present':
if not zone:
return True
if email is not None and zone.email != email:
return True
if description is not None and zone.description != description:
return True
if ttl is not None and zone.ttl != ttl:
return True
if masters is not None and zone.masters != masters:
return True
if state == 'absent' and zone:
return True
return False
class DnsZoneModule(OpenStackModule):
def _wait(timeout, cloud, zone, state, module, sdk):
"""Wait for a zone to reach the desired state for the given state."""
for count in sdk.utils.iterate_timeout(
timeout,
"Timeout waiting for zone to be %s" % state):
if (state == 'absent' and zone is None) or (state == 'present' and zone and zone.status == 'ACTIVE'):
return
try:
zone = cloud.get_zone(zone.id)
except Exception:
continue
if zone and zone.status == 'ERROR':
module.fail_json(msg="Zone reached ERROR state while waiting for it to be %s" % state)
def main():
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
zone_type=dict(required=False, choices=['primary', 'secondary']),
email=dict(required=False, default=None),
description=dict(required=False, default=None),
ttl=dict(required=False, default=None, type='int'),
masters=dict(required=False, default=None, type='list', elements='str'),
state=dict(default='present', choices=['absent', 'present']),
argument_spec = dict(
name=dict(required=True, type='str'),
zone_type=dict(required=False, choices=['primary', 'secondary'], type='str'),
email=dict(required=False, type='str'),
description=dict(required=False, type='str'),
ttl=dict(required=False, type='int'),
masters=dict(required=False, type='list', elements='str'),
state=dict(default='present', choices=['absent', 'present'], type='str'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _system_state_change(self, state, email, description, ttl, masters, zone):
if state == 'present':
if not zone:
return True
if email is not None and zone.email != email:
return True
if description is not None and zone.description != description:
return True
if ttl is not None and zone.ttl != ttl:
return True
if masters is not None and zone.masters != masters:
return True
if state == 'absent' and zone:
return True
return False
name = module.params.get('name')
state = module.params.get('state')
wait = module.params.get('wait')
timeout = module.params.get('timeout')
def _wait(self, timeout, zone, state):
"""Wait for a zone to reach the desired state for the given state."""
sdk, cloud = openstack_cloud_from_module(module)
try:
zone = cloud.get_zone(name)
for count in self.sdk.utils.iterate_timeout(
timeout,
"Timeout waiting for zone to be %s" % state):
if (state == 'absent' and zone is None) or (state == 'present' and zone and zone.status == 'ACTIVE'):
return
try:
zone = self.conn.get_zone(zone.id)
except Exception:
continue
if zone and zone.status == 'ERROR':
self.fail_json(msg="Zone reached ERROR state while waiting for it to be %s" % state)
def run(self):
name = self.params['name']
state = self.params['state']
wait = self.params['wait']
timeout = self.params['timeout']
zone = self.conn.get_zone(name)
if state == 'present':
zone_type = module.params.get('zone_type')
email = module.params.get('email')
description = module.params.get('description')
ttl = module.params.get('ttl')
masters = module.params.get('masters')
if module.check_mode:
module.exit_json(changed=_system_state_change(state, email,
description, ttl,
masters, zone))
zone_type = self.params['zone_type']
email = self.params['email']
description = self.params['description']
ttl = self.params['ttl']
masters = self.params['masters']
kwargs = {}
if email:
kwargs['email'] = email
if description:
kwargs['description'] = description
if ttl:
kwargs['ttl'] = ttl
if masters:
kwargs['masters'] = masters
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, email,
description, ttl,
masters, zone))
if zone is None:
zone = cloud.create_zone(
name=name, zone_type=zone_type, email=email,
description=description, ttl=ttl, masters=masters)
zone = self.conn.create_zone(
name=name, zone_type=zone_type, **kwargs)
changed = True
else:
if masters is None:
masters = []
pre_update_zone = zone
changed = _system_state_change(state, email,
description, ttl,
masters, pre_update_zone)
changed = self._system_state_change(state, email,
description, ttl,
masters, pre_update_zone)
if changed:
zone = cloud.update_zone(
name, email=email,
description=description,
ttl=ttl, masters=masters)
zone = self.conn.update_zone(
name, **kwargs)
if wait:
_wait(timeout, cloud, zone, state, module, sdk)
self._wait(timeout, zone, state)
module.exit_json(changed=changed, zone=zone)
self.exit_json(changed=changed, zone=zone)
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state, None,
None, None,
None, zone))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, None,
None, None,
None, zone))
if zone is None:
changed = False
else:
cloud.delete_zone(name)
self.conn.delete_zone(name)
changed = True
if wait:
_wait(timeout, cloud, zone, state, module, sdk)
self._wait(timeout, zone, state)
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = DnsZoneModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1,177 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: dns_zone_info
short_description: Getting information about dns zones
author: OpenStack Ansible SIG
description:
- Getting information about dns zones. Output can be filtered.
options:
name:
description:
- Zone name.
type: str
type:
description:
- Zone type.
choices: [primary, secondary]
type: str
email:
description:
- Email of the zone owner (only applies if zone_type is primary).
type: str
description:
description:
- Zone description.
type: str
ttl:
description:
- TTL (Time To Live) value in seconds.
type: int
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Create a zone named "example.net"
- openstack.cloud.dns_zones:
'''
RETURN = '''
zone:
description: Dictionary describing the zone.
returned: On success when I(state) is 'present'.
type: complex
contains:
action:
description: Current action in progress on the resource.
type: str
sample: "CREATE"
attributes:
description: Key:Value pairs of information about this zone, and the pool the user would like to place \
the zone in. This information can be used by the scheduler to place zones on the correct pool.
type: dict
sample: {"tier": "gold", "ha": "true"}
created_at:
description: Date / Time when resource was created.
type: str
sample: "2014-07-07T18:25:31.275934"
description:
description: Description for this zone.
type: str
sample: "This is an example zone."
email:
description: E-mail for the zone. Used in SOA records for the zone.
type: str
sample: "test@example.org"
id:
description: ID for the resource.
type: int
sample: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
links:
description: Links to the resource, and other related resources. When a response has been broken into\
pages, we will include a next link that should be followed to retrieve all results.
type: dict
sample: {"self": "https://127.0.0.1:9001/v2/zones/a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"}
masters:
description: Mandatory for secondary zones. The servers to slave from to get DNS information.
type: list
sample: "[]"
name:
description: DNS Name for the zone.
type: str
sample: "test.test."
pool_id:
description: ID for the pool hosting this zone.
type: str
sample: "a86dba58-0043-4cc6-a1bb-69d5e86f3ca3"
project_id:
description: ID for the project that owns the resource.
type: str
sample: "4335d1f0-f793-11e2-b778-0800200c9a66"
serial:
description: Current serial number for the zone.
type: int
sample: 1404757531
status:
description: Status of the resource.
type: str
sample: "ACTIVE"
ttl:
description: TTL (Time to Live) for the zone.
type: int
sample: 7200
type:
description: Type of zone. PRIMARY is controlled by Designate, SECONDARY zones are slaved from another\
DNS Server. Defaults to PRIMARY
type: str
sample: "PRIMARY"
updated_at:
description: Date / Time when resource last updated.
type: str
sample: "2014-07-07T18:25:31.275934"
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class DnsZoneInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, type='str'),
type=dict(required=False, choices=['primary', 'secondary'], type='str'),
email=dict(required=False, type='str'),
description=dict(required=False, type='str'),
ttl=dict(required=False, type='int')
)
def run(self):
name = self.params['name']
type = self.params['type']
email = self.params['email']
description = self.params['description']
ttl = self.params['ttl']
kwargs = {}
if name:
kwargs['name'] = name
if type:
kwargs['type'] = type
if email:
kwargs['email'] = email
if description:
kwargs['description'] = description
if ttl:
kwargs['ttl'] = ttl
data = []
for raw in self.conn.dns.zones(**kwargs):
dt = raw.to_dict()
dt.pop('location')
data.append(dt)
self.exit_json(zones=data, changed=False)
def main():
module = DnsZoneInfoModule()
module()
if __name__ == '__main__':
main()

View File

@@ -103,35 +103,11 @@ endpoint:
sample: True
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, endpoint):
if endpoint.enabled != module.params['enabled']:
return True
if endpoint.url != module.params['url']:
return True
return False
def _system_state_change(module, endpoint):
state = module.params['state']
if state == 'absent' and endpoint:
return True
if state == 'present':
if endpoint is None:
return True
return _needs_update(module, endpoint)
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityEndpointModule(OpenStackModule):
argument_spec = dict(
service=dict(type='str', required=True),
endpoint_interface=dict(type='str', required=True, choices=['admin', 'public', 'internal']),
url=dict(type='str', required=True),
@@ -140,68 +116,89 @@ def main():
state=dict(type='str', default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
service_name_or_id = module.params['service']
interface = module.params['endpoint_interface']
url = module.params['url']
region = module.params['region']
enabled = module.params['enabled']
state = module.params['state']
def _needs_update(self, endpoint):
if endpoint.enabled != self.params['enabled']:
return True
if endpoint.url != self.params['url']:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
def _system_state_change(self, endpoint):
state = self.params['state']
if state == 'absent' and endpoint:
return True
service = cloud.get_service(service_name_or_id)
if service is None:
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
if state == 'present':
if endpoint is None:
return True
return self._needs_update(endpoint)
return False
def run(self):
service_name_or_id = self.params['service']
interface = self.params['endpoint_interface']
url = self.params['url']
region = self.params['region']
enabled = self.params['enabled']
state = self.params['state']
service = self.conn.get_service(service_name_or_id)
if service is None and state == 'absent':
self.exit_json(changed=False)
elif service is None and state == 'present':
self.fail_json(msg='Service %s does not exist' % service_name_or_id)
filters = dict(service_id=service.id, interface=interface)
if region is not None:
filters['region'] = region
endpoints = cloud.search_endpoints(filters=filters)
endpoints = self.conn.search_endpoints(filters=filters)
if len(endpoints) > 1:
module.fail_json(msg='Service %s, interface %s and region %s are '
'not unique' %
(service_name_or_id, interface, region))
self.fail_json(msg='Service %s, interface %s and region %s are '
'not unique' %
(service_name_or_id, interface, region))
elif len(endpoints) == 1:
endpoint = endpoints[0]
else:
endpoint = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, endpoint))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(endpoint))
if state == 'present':
if endpoint is None:
result = cloud.create_endpoint(service_name_or_id=service,
url=url, interface=interface,
region=region, enabled=enabled)
result = self.conn.create_endpoint(
service_name_or_id=service, url=url, interface=interface,
region=region, enabled=enabled)
endpoint = result[0]
changed = True
else:
if _needs_update(module, endpoint):
endpoint = cloud.update_endpoint(
if self._needs_update(endpoint):
endpoint = self.conn.update_endpoint(
endpoint.id, url=url, enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, endpoint=endpoint)
self.exit_json(changed=changed, endpoint=endpoint)
elif state == 'absent':
if endpoint is None:
changed = False
else:
cloud.delete_endpoint(endpoint.id)
self.conn.delete_endpoint(endpoint.id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityEndpointModule()
module()
if __name__ == '__main__':

View File

@@ -72,124 +72,11 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_idp(idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
return None
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
def delete_identity_provider(module, sdk, cloud, idp):
"""
Delete an existing Identity Provider
returns: the "Changed" state
"""
if idp is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_identity_provider(idp)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete identity provider: {0}'.format(str(ex)))
return True
def create_identity_provider(module, sdk, cloud, name):
"""
Create a new Identity Provider
returns: the "Changed" state and the new identity provider
"""
if module.check_mode:
return True, None
description = module.params.get('description')
enabled = module.params.get('enabled')
domain_id = module.params.get('domain_id')
remote_ids = module.params.get('remote_ids')
if enabled is None:
enabled = True
if remote_ids is None:
remote_ids = []
attributes = {
'domain_id': domain_id,
'enabled': enabled,
'remote_ids': remote_ids,
}
if description is not None:
attributes['description'] = description
try:
idp = cloud.identity.create_identity_provider(id=name, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create identity provider: {0}'.format(str(ex)))
return (True, idp)
def update_identity_provider(module, sdk, cloud, idp):
"""
Update an existing Identity Provider
returns: the "Changed" state and the new identity provider
"""
description = module.params.get('description')
enabled = module.params.get('enabled')
domain_id = module.params.get('domain_id')
remote_ids = module.params.get('remote_ids')
attributes = {}
if (description is not None) and (description != idp.description):
attributes['description'] = description
if (enabled is not None) and (enabled != idp.is_enabled):
attributes['enabled'] = enabled
if (domain_id is not None) and (domain_id != idp.domain_id):
attributes['domain_id'] = domain_id
if (remote_ids is not None) and (remote_ids != idp.remote_ids):
attributes['remote_ids'] = remote_ids
if not attributes:
return False, idp
if module.check_mode:
return True, None
try:
new_idp = cloud.identity.update_identity_provider(idp, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update identity provider: {0}'.format(str(ex)))
return (True, new_idp)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
class IdentityFederationIdpModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
description=dict(),
@@ -197,45 +84,136 @@ def main():
enabled=dict(type='bool', aliases=['is_enabled']),
remote_ids=dict(type='list', elements='str'),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
module_kwargs = dict(
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
state = module.params.get('state')
changed = False
def normalize_idp(self, idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
idp = cloud.identity.get_identity_provider(name)
except sdk.exceptions.ResourceNotFound:
idp = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
if state == 'absent':
if idp is not None:
changed = delete_identity_provider(module, sdk, cloud, idp)
module.exit_json(changed=changed)
# state == 'present'
else:
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
if module.params.get('domain_id') is None:
module.fail_json(msg='A domain_id must be passed when creating'
' an identity provider')
(changed, idp) = create_identity_provider(module, sdk, cloud, name)
idp = normalize_idp(idp)
module.exit_json(changed=changed, identity_provider=idp)
return None
(changed, new_idp) = update_identity_provider(module, sdk, cloud, idp)
new_idp = normalize_idp(new_idp)
module.exit_json(changed=changed, identity_provider=new_idp)
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
def delete_identity_provider(self, idp):
"""
Delete an existing Identity Provider
returns: the "Changed" state
"""
if idp is None:
return False
if self.ansible.check_mode:
return True
self.conn.identity.delete_identity_provider(idp)
return True
def create_identity_provider(self, name):
"""
Create a new Identity Provider
returns: the "Changed" state and the new identity provider
"""
if self.ansible.check_mode:
return True, None
description = self.params.get('description')
enabled = self.params.get('enabled')
domain_id = self.params.get('domain_id')
remote_ids = self.params.get('remote_ids')
if enabled is None:
enabled = True
if remote_ids is None:
remote_ids = []
attributes = {
'domain_id': domain_id,
'enabled': enabled,
'remote_ids': remote_ids,
}
if description is not None:
attributes['description'] = description
idp = self.conn.identity.create_identity_provider(id=name, **attributes)
return (True, idp)
def update_identity_provider(self, idp):
"""
Update an existing Identity Provider
returns: the "Changed" state and the new identity provider
"""
description = self.params.get('description')
enabled = self.params.get('enabled')
domain_id = self.params.get('domain_id')
remote_ids = self.params.get('remote_ids')
attributes = {}
if (description is not None) and (description != idp.description):
attributes['description'] = description
if (enabled is not None) and (enabled != idp.is_enabled):
attributes['enabled'] = enabled
if (domain_id is not None) and (domain_id != idp.domain_id):
attributes['domain_id'] = domain_id
if (remote_ids is not None) and (remote_ids != idp.remote_ids):
attributes['remote_ids'] = remote_ids
if not attributes:
return False, idp
if self.ansible.check_mode:
return True, None
new_idp = self.conn.identity.update_identity_provider(idp, **attributes)
return (True, new_idp)
def run(self):
""" Module entry point """
name = self.params.get('name')
state = self.params.get('state')
changed = False
idp = self.conn.identity.find_identity_provider(name)
if state == 'absent':
if idp is not None:
changed = self.delete_identity_provider(idp)
self.exit_json(changed=changed)
# state == 'present'
else:
if idp is None:
if self.params.get('domain_id') is None:
self.fail_json(msg='A domain_id must be passed when creating'
' an identity provider')
(changed, idp) = self.create_identity_provider(name)
idp = self.normalize_idp(idp)
self.exit_json(changed=changed, identity_provider=idp)
(changed, new_idp) = self.update_identity_provider(idp)
new_idp = self.normalize_idp(new_idp)
self.exit_json(changed=changed, identity_provider=new_idp)
def main():
module = IdentityFederationIdpModule()
module()
if __name__ == '__main__':

View File

@@ -39,62 +39,50 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_idp(idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
class IdentityFederationIdpInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(aliases=['id']),
)
module_kwargs = dict(
supports_check_mode=True
)
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
return
def normalize_idp(self, idp):
"""
Normalizes the IDP definitions so that the outputs are consistent with the
parameters
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
- "enabled" (parameter) == "is_enabled" (SDK)
- "name" (parameter) == "id" (SDK)
"""
if idp is None:
return
_idp = idp.to_dict()
_idp['enabled'] = idp['is_enabled']
_idp['name'] = idp['id']
return _idp
def run(self):
""" Module entry point """
name = self.params.get('name')
if name:
idp = self.normalize_idp(self.conn.identity.get_identity_provider(name))
self.exit_json(changed=False, identity_providers=[idp])
else:
providers = list(map(self.normalize_idp, self.conn.identity.identity_providers()))
self.exit_json(changed=False, identity_providers=providers)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(aliases=['id']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
if name:
try:
idp = normalize_idp(cloud.identity.get_identity_provider(name))
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find identity provider')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get identity provider: {0}'.format(str(ex)))
module.exit_json(changed=False, identity_providers=[idp])
else:
try:
providers = list(map(normalize_idp, cloud.identity.identity_providers()))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list identity providers: {0}'.format(str(ex)))
module.exit_json(changed=False, identity_providers=providers)
module = IdentityFederationIdpInfoModule()
module()
if __name__ == '__main__':

View File

@@ -78,93 +78,11 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_mapping(mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with
the parameters
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
return None
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def create_mapping(module, sdk, cloud, name):
"""
Attempt to create a Mapping
returns: A tuple containing the "Changed" state and the created mapping
"""
if module.check_mode:
return (True, None)
rules = module.params.get('rules')
try:
mapping = cloud.identity.create_mapping(id=name, rules=rules)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create mapping: {0}'.format(str(ex)))
return (True, mapping)
def delete_mapping(module, sdk, cloud, mapping):
"""
Attempt to delete a Mapping
returns: the "Changed" state
"""
if mapping is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_mapping(mapping)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete mapping: {0}'.format(str(ex)))
return True
def update_mapping(module, sdk, cloud, mapping):
"""
Attempt to delete a Mapping
returns: The "Changed" state and the the new mapping
"""
current_rules = mapping.rules
new_rules = module.params.get('rules')
# Nothing to do
if current_rules == new_rules:
return (False, mapping)
if module.check_mode:
return (True, None)
try:
new_mapping = cloud.identity.update_mapping(mapping, rules=new_rules)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update mapping: {0}'.format(str(ex)))
return (True, new_mapping)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
class IdentityFederationMappingModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
rules=dict(type='list', elements='dict', options=dict(
@@ -172,46 +90,107 @@ def main():
remote=dict(required=True, type='list', elements='dict')
)),
)
module_kwargs = openstack_module_kwargs(
required_if=[('state', 'present', ['rules'])]
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
module_kwargs = dict(
required_if=[('state', 'present', ['rules'])],
supports_check_mode=True
)
name = module.params.get('name')
state = module.params.get('state')
changed = False
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
mapping = cloud.identity.get_mapping(name)
except sdk.exceptions.ResourceNotFound:
mapping = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to fetch mapping: {0}'.format(str(ex)))
if state == 'absent':
if mapping is not None:
changed = delete_mapping(module, sdk, cloud, mapping)
module.exit_json(changed=changed)
# state == 'present'
else:
if len(module.params.get('rules')) < 1:
module.fail_json(msg='At least one rule must be passed')
def normalize_mapping(self, mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with
the parameters
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
(changed, mapping) = create_mapping(module, sdk, cloud, name)
mapping = normalize_mapping(mapping)
module.exit_json(changed=changed, mapping=mapping)
return None
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def create_mapping(self, name):
"""
Attempt to create a Mapping
returns: A tuple containing the "Changed" state and the created mapping
"""
if self.ansible.check_mode:
return (True, None)
rules = self.params.get('rules')
mapping = self.conn.identity.create_mapping(id=name, rules=rules)
return (True, mapping)
def delete_mapping(self, mapping):
"""
Attempt to delete a Mapping
returns: the "Changed" state
"""
if mapping is None:
return False
if self.ansible.check_mode:
return True
self.conn.identity.delete_mapping(mapping)
return True
def update_mapping(self, mapping):
"""
Attempt to delete a Mapping
returns: The "Changed" state and the the new mapping
"""
current_rules = mapping.rules
new_rules = self.params.get('rules')
# Nothing to do
if current_rules == new_rules:
return (False, mapping)
if self.ansible.check_mode:
return (True, None)
new_mapping = self.conn.identity.update_mapping(mapping, rules=new_rules)
return (True, new_mapping)
def run(self):
""" Module entry point """
name = self.params.get('name')
state = self.params.get('state')
changed = False
mapping = self.conn.identity.find_mapping(name)
if state == 'absent':
if mapping is not None:
changed = self.delete_mapping(mapping)
self.exit_json(changed=changed)
# state == 'present'
else:
(changed, new_mapping) = update_mapping(module, sdk, cloud, mapping)
new_mapping = normalize_mapping(new_mapping)
module.exit_json(mapping=new_mapping, changed=changed)
if len(self.params.get('rules')) < 1:
self.fail_json(msg='At least one rule must be passed')
if mapping is None:
(changed, mapping) = self.create_mapping(name)
mapping = self.normalize_mapping(mapping)
self.exit_json(changed=changed, mapping=mapping)
else:
(changed, new_mapping) = self.update_mapping(mapping)
new_mapping = self.normalize_mapping(new_mapping)
self.exit_json(mapping=new_mapping, changed=changed)
def main():
module = IdentityFederationMappingModule()
module()
if __name__ == '__main__':

View File

@@ -38,60 +38,50 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_mapping(mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with the
parameters
class IdentityFederationMappingInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(aliases=['id']),
)
module_kwargs = dict(
supports_check_mode=True
)
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
return None
module_min_sdk_version = "0.44"
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def normalize_mapping(self, mapping):
"""
Normalizes the mapping definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if mapping is None:
return None
_mapping = mapping.to_dict()
_mapping['name'] = mapping['id']
return _mapping
def run(self):
""" Module entry point """
name = self.params.get('name')
if name:
mapping = self.normalize_mapping(
self.conn.identity.get_mapping(name))
self.exit_json(changed=False, mappings=[mapping])
else:
mappings = list(map(
self.normalize_mapping, self.conn.identity.mappings()))
self.exit_json(changed=False, mappings=mappings)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
name=dict(aliases=['id']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
)
name = module.params.get('name')
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
if name:
try:
mapping = normalize_mapping(cloud.identity.get_mapping(name))
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find mapping')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get mapping: {0}'.format(str(ex)))
module.exit_json(changed=False, mappings=[mapping])
else:
try:
mappings = list(map(normalize_mapping, cloud.identity.mappings()))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list mappings: {0}'.format(str(ex)))
module.exit_json(changed=False, mappings=mappings)
module = IdentityFederationMappingInfoModule()
module()
if __name__ == '__main__':

View File

@@ -118,23 +118,12 @@ EXAMPLES = '''
server: cattle001
'''
from ansible.module_utils.basic import AnsibleModule, remove_values
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible.module_utils.basic import remove_values
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _get_floating_ip(cloud, floating_ip_address):
f_ips = cloud.search_floating_ips(
filters={'floating_ip_address': floating_ip_address})
if not f_ips:
return None
return f_ips[0]
def main():
argument_spec = openstack_full_argument_spec(
class NetworkingFloatingIPModule(OpenStackModule):
argument_spec = dict(
server=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
network=dict(required=False, default=None),
@@ -148,107 +137,115 @@ def main():
purge=dict(required=False, type='bool', default=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
module_kwargs = dict()
server_name_or_id = module.params['server']
state = module.params['state']
network = module.params['network']
floating_ip_address = module.params['floating_ip_address']
reuse = module.params['reuse']
fixed_address = module.params['fixed_address']
nat_destination = module.params['nat_destination']
wait = module.params['wait']
timeout = module.params['timeout']
purge = module.params['purge']
def _get_floating_ip(self, floating_ip_address):
f_ips = self.conn.search_floating_ips(
filters={'floating_ip_address': floating_ip_address})
if not f_ips:
return None
sdk, cloud = openstack_cloud_from_module(module)
try:
return f_ips[0]
server = cloud.get_server(server_name_or_id)
def run(self):
server_name_or_id = self.params['server']
state = self.params['state']
network = self.params['network']
floating_ip_address = self.params['floating_ip_address']
reuse = self.params['reuse']
fixed_address = self.params['fixed_address']
nat_destination = self.params['nat_destination']
wait = self.params['wait']
timeout = self.params['timeout']
purge = self.params['purge']
server = self.conn.get_server(server_name_or_id)
if server is None:
module.fail_json(
self.fail_json(
msg="server {0} not found".format(server_name_or_id))
if state == 'present':
# If f_ip already assigned to server, check that it matches
# requirements.
public_ip = cloud.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, public_ip) if public_ip else public_ip
public_ip = self.conn.get_server_public_ip(server)
f_ip = self._get_floating_ip(public_ip) if public_ip else public_ip
if f_ip:
if network:
network_id = cloud.get_network(name_or_id=network)["id"]
network_id = self.conn.get_network(name_or_id=network)["id"]
else:
network_id = None
# check if we have floating ip on given nat_destination network
if nat_destination:
nat_floating_addrs = [
addr for addr in server.addresses.get(
cloud.get_network(nat_destination)['name'], [])
self.conn.get_network(nat_destination)['name'], [])
if addr['addr'] == public_ip
and addr['OS-EXT-IPS:type'] == 'floating'
]
if len(nat_floating_addrs) == 0:
module.fail_json(msg="server {server} already has a "
"floating-ip on a different "
"nat-destination than '{nat_destination}'"
.format(server=server_name_or_id,
nat_destination=nat_destination))
self.fail_json(
msg="server {server} already has a "
"floating-ip on a different "
"nat-destination than '{nat_destination}'"
.format(server=server_name_or_id,
nat_destination=nat_destination))
if all([fixed_address, f_ip.fixed_ip_address == fixed_address,
network, f_ip.network != network_id]):
# Current state definitely conflicts with requirements
module.fail_json(msg="server {server} already has a "
"floating-ip on requested "
"interface but it doesn't match "
"requested network {network}: {fip}"
.format(server=server_name_or_id,
network=network,
fip=remove_values(f_ip,
module.no_log_values)))
self.fail_json(
msg="server {server} already has a "
"floating-ip on requested "
"interface but it doesn't match "
"requested network {network}: {fip}"
.format(server=server_name_or_id,
network=network,
fip=remove_values(f_ip, self.no_log_values)))
if not network or f_ip.network == network_id:
# Requirements are met
module.exit_json(changed=False, floating_ip=f_ip)
self.exit_json(changed=False, floating_ip=f_ip)
# Requirements are vague enough to ignore existing f_ip and try
# to create a new f_ip to the server.
server = cloud.add_ips_to_server(
server = self.conn.add_ips_to_server(
server=server, ips=floating_ip_address, ip_pool=network,
reuse=reuse, fixed_address=fixed_address, wait=wait,
timeout=timeout, nat_destination=nat_destination)
fip_address = cloud.get_server_public_ip(server)
fip_address = self.conn.get_server_public_ip(server)
# Update the floating IP status
f_ip = _get_floating_ip(cloud, fip_address)
module.exit_json(changed=True, floating_ip=f_ip)
f_ip = self._get_floating_ip(fip_address)
self.exit_json(changed=True, floating_ip=f_ip)
elif state == 'absent':
if floating_ip_address is None:
if not server_name_or_id:
module.fail_json(msg="either server or floating_ip_address are required")
server = cloud.get_server(server_name_or_id)
floating_ip_address = cloud.get_server_public_ip(server)
self.fail_json(msg="either server or floating_ip_address are required")
server = self.conn.get_server(server_name_or_id)
floating_ip_address = self.conn.get_server_public_ip(server)
f_ip = _get_floating_ip(cloud, floating_ip_address)
f_ip = self._get_floating_ip(floating_ip_address)
if not f_ip:
# Nothing to detach
module.exit_json(changed=False)
self.exit_json(changed=False)
changed = False
if f_ip["fixed_ip_address"]:
cloud.detach_ip_from_server(
self.conn.detach_ip_from_server(
server_id=server['id'], floating_ip_id=f_ip['id'])
# Update the floating IP status
f_ip = cloud.get_floating_ip(id=f_ip['id'])
f_ip = self.conn.get_floating_ip(id=f_ip['id'])
changed = True
if purge:
cloud.delete_floating_ip(f_ip['id'])
module.exit_json(changed=True)
module.exit_json(changed=changed, floating_ip=f_ip)
self.conn.delete_floating_ip(f_ip['id'])
self.exit_json(changed=True)
self.exit_json(changed=changed, floating_ip=f_ip)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
def main():
module = NetworkingFloatingIPModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1,211 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: floating_ip_info
short_description: Get information about floating ips
author: OpenStack Ansible SIG
description:
- Get a generator of floating ips.
options:
description:
description:
- The description of a floating IP.
type: str
fixed_ip_address:
description:
- The fixed IP address associated with a floating IP address.
type: str
floating_ip_address:
description:
- The IP address of a floating IP.
type: str
floating_network:
description:
- The name or id of the network associated with a floating IP.
type: str
port:
description:
- The name or id of the port to which a floating IP is associated.
type: str
project_id:
description:
- The ID of the project a floating IP is associated with.
type: str
router:
description:
- The name or id of an associated router.
type: str
status:
description:
- The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.
choices: ['active', 'down']
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
floating_ips:
description: The floating ip objects list.
type: complex
returned: On Success.
contains:
created_at:
description: Timestamp at which the floating IP was assigned.
type: str
description:
description: The description of a floating IP.
type: str
dns_domain:
description: The DNS domain.
type: str
dns_name:
description: The DNS name.
type: str
fixed_ip_address:
description: The fixed IP address associated with a floating IP address.
type: str
floating_ip_address:
description: The IP address of a floating IP.
type: str
floating_network_id:
description: The id of the network associated with a floating IP.
type: str
id:
description: Id of the floating ip.
type: str
name:
description: Name of the floating ip.
type: str
port_details:
description: The details of the port that this floating IP associates \
with. Present if ``fip-port-details`` extension is loaded.
type: str
port_id:
description: The port ID floating ip associated with.
type: str
project_id:
description: The ID of the project this floating IP is associated with.
type: str
qos_policy_id:
description: The ID of the QoS policy attached to the floating IP.
type: str
revision_number:
description: Revision number.
type: str
router_id:
description: The id of the router floating ip associated with.
type: str
status:
description: The status of a floating IP, which can be ``ACTIVE``or ``DOWN``.\
Can be 'ACTIVE' and 'DOWN'.
type: str
subnet_id:
description: The id of the subnet the floating ip associated with.
type: str
tags:
description: List of tags.
type: str
updated_at:
description: Timestamp at which the floating IP was last updated.
type: str
'''
EXAMPLES = '''
# Getting all floating ips
- openstack.cloud.floating_ip_info:
register: fips
# Getting fip by associated fixed IP address.
- openstack.cloud.floating_ip_info:
fixed_ip_address: 192.168.10.8
register: fip
# Getting fip by associated router.
- openstack.cloud.floating_ip_info:
router: my-router
register: fip
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class FloatingIPInfoModule(OpenStackModule):
argument_spec = dict(
description=dict(required=False),
fixed_ip_address=dict(required=False),
floating_ip_address=dict(required=False),
floating_network=dict(required=False),
port=dict(required=False),
project_id=dict(required=False),
router=dict(required=False),
status=dict(required=False, choices=['active', 'down']),
)
def run(self):
description = self.params['description']
fixed_ip_address = self.params['fixed_ip_address']
floating_ip_address = self.params['floating_ip_address']
floating_network = self.params['floating_network']
port = self.params['port']
project_id = self.params['project_id']
router = self.params['router']
status = self.params['status']
data = []
query = {}
if description:
query['description'] = description
if fixed_ip_address:
query['fixed_ip_address'] = fixed_ip_address
if floating_ip_address:
query['floating_ip_address'] = floating_ip_address
if floating_network:
try:
query['floating_network_id'] = self.conn.network.find_network(name_or_id=floating_network,
ignore_missing=False).id
except self.sdk.exceptions.ResourceNotFound:
self.fail_json(msg="floating_network not found")
if port:
try:
query['port_id'] = self.conn.network.find_port(name_or_id=port, ignore_missing=False).id
except self.sdk.exceptions.ResourceNotFound:
self.fail_json(msg="port not found")
if project_id:
query['project_id'] = project_id
if router:
try:
query['router_id'] = self.conn.network.find_router(name_or_id=router, ignore_missing=False).id
except self.sdk.exceptions.ResourceNotFound:
self.fail_json(msg="router not found")
if status:
query['status'] = status.upper()
for raw in self.conn.network.ips(**query):
dt = raw.to_dict()
dt.pop('location')
data.append(dt)
self.exit_json(
changed=False,
floating_ips=data
)
def main():
module = FloatingIPInfoModule()
module()
if __name__ == '__main__':
main()

View File

@@ -42,58 +42,54 @@ EXAMPLES = '''
group: demo
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, in_group):
if state == 'present' and not in_group:
return True
if state == 'absent' and in_group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityGroupAssignment(OpenStackModule):
argument_spec = dict(
user=dict(required=True),
group=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
user = module.params['user']
group = module.params['group']
state = module.params['state']
def _system_state_change(self, state, in_group):
if state == 'present' and not in_group:
return True
if state == 'absent' and in_group:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
in_group = cloud.is_user_in_group(user, group)
def run(self):
user = self.params['user']
group = self.params['group']
state = self.params['state']
if module.check_mode:
module.exit_json(changed=_system_state_change(state, in_group))
in_group = self.conn.is_user_in_group(user, group)
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, in_group))
changed = False
if state == 'present':
if not in_group:
cloud.add_user_to_group(user, group)
self.conn.add_user_to_group(user, group)
changed = True
elif state == 'absent':
if in_group:
cloud.remove_user_from_group(user, group)
self.conn.remove_user_from_group(user, group)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
def main():
module = IdentityGroupAssignment()
module()
if __name__ == '__main__':

View File

@@ -77,69 +77,11 @@ RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, aggregate):
new_metadata = (module.params['metadata'] or {})
if module.params['availability_zone'] is not None:
new_metadata['availability_zone'] = module.params['availability_zone']
if module.params['name'] != aggregate.name:
return True
if module.params['hosts'] is not None:
if module.params['purge_hosts']:
if set(module.params['hosts']) != set(aggregate.hosts):
return True
else:
intersection = set(module.params['hosts']).intersection(set(aggregate.hosts))
if set(module.params['hosts']) != intersection:
return True
if module.params['availability_zone'] is not None:
if module.params['availability_zone'] != aggregate.availability_zone:
return True
if module.params['metadata'] is not None:
if new_metadata != aggregate.metadata:
return True
return False
def _system_state_change(module, aggregate):
state = module.params['state']
if state == 'absent' and aggregate:
return True
if state == 'present':
if aggregate is None:
return True
return _needs_update(module, aggregate)
return False
def _update_hosts(cloud, aggregate, hosts, purge_hosts):
if hosts is None:
return
hosts_to_add = set(hosts) - set(aggregate.hosts)
for i in hosts_to_add:
cloud.add_host_to_aggregate(aggregate.id, i)
if not purge_hosts:
return
hosts_to_remove = set(aggregate.hosts) - set(hosts)
for i in hosts_to_remove:
cloud.remove_host_from_aggregate(aggregate.id, i)
def main():
argument_spec = openstack_full_argument_spec(
class ComputeHostAggregateModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
metadata=dict(required=False, default=None, type='dict'),
availability_zone=dict(required=False, default=None),
@@ -148,24 +90,74 @@ def main():
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
name = module.params['name']
metadata = module.params['metadata']
availability_zone = module.params['availability_zone']
hosts = module.params['hosts']
purge_hosts = module.params['purge_hosts']
state = module.params['state']
def _needs_update(self, aggregate):
new_metadata = (self.params['metadata'] or {})
if metadata is not None:
metadata.pop('availability_zone', None)
if self.params['availability_zone'] is not None:
new_metadata['availability_zone'] = self.params['availability_zone']
sdk, cloud = openstack_cloud_from_module(module)
try:
aggregates = cloud.search_aggregates(name_or_id=name)
if self.params['name'] != aggregate.name:
return True
if self.params['hosts'] is not None:
if self.params['purge_hosts']:
if set(self.params['hosts']) != set(aggregate.hosts):
return True
else:
intersection = set(self.params['hosts']).intersection(set(aggregate.hosts))
if set(self.params['hosts']) != intersection:
return True
if self.params['availability_zone'] is not None:
if self.params['availability_zone'] != aggregate.availability_zone:
return True
if self.params['metadata'] is not None:
if new_metadata != aggregate.metadata:
return True
return False
def _system_state_change(self, aggregate):
state = self.params['state']
if state == 'absent' and aggregate:
return True
if state == 'present':
if aggregate is None:
return True
return self._needs_update(aggregate)
return False
def _update_hosts(self, aggregate, hosts, purge_hosts):
if hosts is None:
return
hosts_to_add = set(hosts) - set(aggregate.get("hosts", []))
for i in hosts_to_add:
self.conn.add_host_to_aggregate(aggregate.id, i)
if not purge_hosts:
return
hosts_to_remove = set(aggregate.get("hosts", [])) - set(hosts)
for i in hosts_to_remove:
self.conn.remove_host_from_aggregate(aggregate.id, i)
def run(self):
name = self.params['name']
metadata = self.params['metadata']
availability_zone = self.params['availability_zone']
hosts = self.params['hosts']
purge_hosts = self.params['purge_hosts']
state = self.params['state']
if metadata is not None:
metadata.pop('availability_zone', None)
aggregates = self.conn.search_aggregates(name_or_id=name)
if len(aggregates) == 1:
aggregate = aggregates[0]
@@ -174,45 +166,48 @@ def main():
else:
raise Exception("Should not happen")
if module.check_mode:
module.exit_json(changed=_system_state_change(module, aggregate))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(aggregate))
if state == 'present':
if aggregate is None:
aggregate = cloud.create_aggregate(name=name,
availability_zone=availability_zone)
_update_hosts(cloud, aggregate, hosts, False)
aggregate = self.conn.create_aggregate(
name=name, availability_zone=availability_zone)
self._update_hosts(aggregate, hosts, False)
if metadata:
cloud.set_aggregate_metadata(aggregate.id, metadata)
self.conn.set_aggregate_metadata(aggregate.id, metadata)
changed = True
else:
if _needs_update(module, aggregate):
if self._needs_update(aggregate):
if availability_zone is not None:
aggregate = cloud.update_aggregate(aggregate.id, name=name,
availability_zone=availability_zone)
aggregate = self.conn.update_aggregate(
aggregate.id, name=name,
availability_zone=availability_zone)
if metadata is not None:
metas = metadata
for i in (set(aggregate.metadata.keys()) - set(metadata.keys())):
if i != 'availability_zone':
metas[i] = None
cloud.set_aggregate_metadata(aggregate.id, metas)
_update_hosts(cloud, aggregate, hosts, purge_hosts)
self.conn.set_aggregate_metadata(aggregate.id, metas)
self._update_hosts(aggregate, hosts, purge_hosts)
changed = True
else:
changed = False
module.exit_json(changed=changed)
self.exit_json(changed=changed)
elif state == 'absent':
if aggregate is None:
changed = False
else:
_update_hosts(cloud, aggregate, [], True)
cloud.delete_aggregate(aggregate.id)
self._update_hosts(aggregate, [], True)
self.conn.delete_aggregate(aggregate.id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = ComputeHostAggregateModule()
module()
if __name__ == '__main__':

View File

@@ -85,92 +85,86 @@ id:
sample: "474acfe5-be34-494c-b339-50f06aa143e4"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, domain):
if module.params['description'] is not None and \
domain.description != module.params['description']:
return True
if domain.enabled != module.params['enabled']:
return True
return False
def _system_state_change(module, domain):
state = module.params['state']
if state == 'absent' and domain:
return True
if state == 'present':
if domain is None:
return True
return _needs_update(module, domain)
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityDomainModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
description=dict(default=None),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
name = module.params['name']
description = module.params['description']
enabled = module.params['enabled']
state = module.params['state']
def _needs_update(self, domain):
if self.params['description'] is not None and \
domain.description != self.params['description']:
return True
if domain.enabled != self.params['enabled']:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
def _system_state_change(self, domain):
state = self.params['state']
if state == 'absent' and domain:
return True
domains = cloud.search_domains(filters=dict(name=name))
if state == 'present':
if domain is None:
return True
return self._needs_update(domain)
return False
def run(self):
name = self.params['name']
description = self.params['description']
enabled = self.params['enabled']
state = self.params['state']
domains = self.conn.search_domains(filters=dict(name=name))
if len(domains) > 1:
module.fail_json(msg='Domain name %s is not unique' % name)
self.fail_json(msg='Domain name %s is not unique' % name)
elif len(domains) == 1:
domain = domains[0]
else:
domain = None
if module.check_mode:
module.exit_json(changed=_system_state_change(module, domain))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(domain))
if state == 'present':
if domain is None:
domain = cloud.create_domain(
domain = self.conn.create_domain(
name=name, description=description, enabled=enabled)
changed = True
else:
if _needs_update(module, domain):
domain = cloud.update_domain(
if self._needs_update(domain):
domain = self.conn.update_domain(
domain.id, name=name, description=description,
enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, domain=domain, id=domain.id)
self.exit_json(changed=changed, domain=domain, id=domain.id)
elif state == 'absent':
if domain is None:
changed = False
else:
cloud.delete_domain(domain.id)
self.conn.delete_domain(domain.id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityDomainModule()
module()
if __name__ == '__main__':

View File

@@ -81,52 +81,42 @@ openstack_domains:
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class IdentityDomainInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[
['name', 'filters'],
]
)
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.identity_domain_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.identity_domain_facts' module has been renamed to 'openstack.cloud.identity_domain_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
filters = module.params['filters']
deprecated_names = ('openstack.cloud.identity_domain_facts')
def run(self):
name = self.params['name']
filters = self.params['filters']
if name:
# Let's suppose user is passing domain ID
try:
domains = opcloud.get_domain(name)
domains = self.conn.get_domain(name)
except Exception:
domains = opcloud.search_domains(filters={'name': name})
domains = self.conn.search_domains(filters={'name': name})
else:
domains = opcloud.search_domains(filters)
domains = self.conn.search_domains(filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_domains=domains))
else:
module.exit_json(changed=False, openstack_domains=domains)
self.exit_json(changed=False, openstack_domains=domains)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityDomainInfoModule()
module()
if __name__ == '__main__':

View File

@@ -86,75 +86,71 @@ group:
sample: "default"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, description, group):
if state == 'present' and not group:
return True
if state == 'present' and description is not None and group.description != description:
return True
if state == 'absent' and group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityGroupModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
description=dict(required=False, default=None),
domain_id=dict(required=False, default=None),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
name = module.params.get('name')
description = module.params.get('description')
state = module.params.get('state')
def _system_state_change(self, state, description, group):
if state == 'present' and not group:
return True
if state == 'present' and description is not None and group.description != description:
return True
if state == 'absent' and group:
return True
return False
domain_id = module.params.pop('domain_id')
def run(self):
name = self.params.get('name')
description = self.params.get('description')
state = self.params.get('state')
domain_id = self.params.pop('domain_id')
sdk, cloud = openstack_cloud_from_module(module)
try:
if domain_id:
group = cloud.get_group(name, filters={'domain_id': domain_id})
group = self.conn.get_group(name, filters={'domain_id': domain_id})
else:
group = cloud.get_group(name)
group = self.conn.get_group(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, description, group))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, description, group))
if state == 'present':
if group is None:
group = cloud.create_group(
group = self.conn.create_group(
name=name, description=description, domain=domain_id)
changed = True
else:
if description is not None and group.description != description:
group = cloud.update_group(
group = self.conn.update_group(
group.id, description=description)
changed = True
else:
changed = False
module.exit_json(changed=changed, group=group)
self.exit_json(changed=changed, group=group)
elif state == 'absent':
if group is None:
changed = False
else:
cloud.delete_group(group.id)
self.conn.delete_group(group.id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityGroupModule()
module()
if __name__ == '__main__':

View File

@@ -110,48 +110,45 @@ openstack_groups:
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class IdentityGroupInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module = AnsibleModule(argument_spec)
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
def run(self):
name = self.params['name']
domain = self.params['domain']
filters = self.params['filters']
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
dom = self.conn.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
self.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
groups = opcloud.search_groups(name, filters, domain_id=domain)
module.exit_json(changed=False, groups=groups)
groups = self.conn.search_groups(name, filters, domain_id=domain)
self.exit_json(changed=False, groups=groups)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityGroupInfoModule()
module()
if __name__ == '__main__':

View File

@@ -59,58 +59,54 @@ role:
sample: "demo"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, role):
if state == 'present' and not role:
return True
if state == 'absent' and role:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class IdentityRoleModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
supports_check_mode=True
)
name = module.params.get('name')
state = module.params.get('state')
def _system_state_change(self, state, role):
if state == 'present' and not role:
return True
if state == 'absent' and role:
return True
return False
sdk, cloud = openstack_cloud_from_module(module)
try:
role = cloud.get_role(name)
def run(self):
name = self.params.get('name')
state = self.params.get('state')
if module.check_mode:
module.exit_json(changed=_system_state_change(state, role))
role = self.conn.get_role(name)
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, role))
if state == 'present':
if role is None:
role = cloud.create_role(name)
role = self.conn.create_role(name)
changed = True
else:
changed = False
module.exit_json(changed=changed, role=role)
self.exit_json(changed=changed, role=role)
elif state == 'absent':
if role is None:
changed = False
else:
cloud.delete_role(name)
self.conn.delete_role(name)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityRoleModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1,103 @@
#!/usr/bin/python
# coding: utf-8 -*-
# Copyright (c) 2020, Sagi Shnaidman <sshnaidm@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: identity_role_info
short_description: Retrive information about roles
author: OpenStack Ansible SIG
description:
- Get information about identity roles in Openstack
options:
domain_id:
description:
- List roles in specified domain only
type: str
required: false
name:
description:
- List role speficied by name
type: str
required: false
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
openstack_roles:
description: List of identity roles
returned: always
type: list
elements: dict
sample:
- domain_id: None
id: 19bf514fdda84f808ccee8463bd85c1a
location:
cloud: mycloud
project:
domain_id: None
domain_name: None
id: None
name: None
region_name: None
zone: None
name: member
properties:
'''
EXAMPLES = '''
# Retrieve info about all roles
- openstack.cloud.identity_role_info:
cloud: mycloud
# Retrieve info about all roles in specific domain
- openstack.cloud.identity_role_info:
cloud: mycloud
domain_id: some_domain_id
# Retrieve info about role 'admin'
- openstack.cloud.identity_role_info:
cloud: mycloud
name: admin
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class IdentityRoleInfoModule(OpenStackModule):
argument_spec = dict(
domain_id=dict(type='str', required=False),
name=dict(type='str', required=False),
)
module_kwargs = dict(
supports_check_mode=True,
)
def run(self):
roles = self.conn.list_roles(domain_id=self.params['domain_id'])
# Dictionaries are supported from Train release
roles = [item if isinstance(item, dict) else item.to_dict() for item in roles]
# Filtering by name is supported from Wallaby release
if self.params['name']:
roles = [item for item in roles if self.params['name'] in (item['id'], item['name'])]
self.results.update({'openstack_roles': roles})
def main():
module = IdentityRoleInfoModule()
module()
if __name__ == '__main__':
main()

View File

@@ -131,54 +131,12 @@ user:
type: str
sample: "demouser"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(params_dict, user):
for k in params_dict:
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
return True
# We don't get password back in the user object, so assume any supplied
# password is a change.
if (
params_dict['password'] is not None
and params_dict['update_password'] == 'always'
):
return True
return False
def _get_domain_id(cloud, domain):
try:
# We assume admin is passing domain id
domain_id = cloud.get_domain(domain)['id']
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
try:
domain_id = cloud.search_domains(filters={'name': domain})[0]['id']
except Exception:
# Ok, let's hope the user is non-admin and passing a sane id
domain_id = domain
return domain_id
def _get_default_project_id(cloud, default_project, domain_id, module):
project = cloud.get_project(default_project, domain_id=domain_id)
if not project:
module.fail_json(msg='Default project %s is not valid' % default_project)
return project['id']
def main():
argument_spec = openstack_full_argument_spec(
class IdentityUserModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
password=dict(required=False, default=None, no_log=True),
email=dict(required=False, default=None),
@@ -190,47 +148,81 @@ def main():
update_password=dict(default=None, choices=['always', 'on_create']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
**module_kwargs)
module_kwargs = dict()
name = module.params['name']
password = module.params.get('password')
email = module.params['email']
default_project = module.params['default_project']
domain = module.params['domain']
enabled = module.params['enabled']
state = module.params['state']
update_password = module.params['update_password']
description = module.params['description']
def _needs_update(self, params_dict, user):
for k in params_dict:
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
return True
# We don't get password back in the user object, so assume any supplied
# password is a change.
if (
params_dict['password'] is not None
and params_dict['update_password'] == 'always'
):
return True
return False
def _get_domain_id(self, domain):
try:
# We assume admin is passing domain id
domain_id = self.conn.get_domain(domain)['id']
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
try:
domain_id = self.conn.search_domains(filters={'name': domain})[0]['id']
except Exception:
# Ok, let's hope the user is non-admin and passing a sane id
domain_id = domain
return domain_id
def _get_default_project_id(self, default_project, domain_id):
project = self.conn.get_project(default_project, domain_id=domain_id)
if not project:
self.fail_json(msg='Default project %s is not valid' % default_project)
return project['id']
def run(self):
name = self.params['name']
password = self.params.get('password')
email = self.params['email']
default_project = self.params['default_project']
domain = self.params['domain']
enabled = self.params['enabled']
state = self.params['state']
update_password = self.params['update_password']
description = self.params['description']
sdk, cloud = openstack_cloud_from_module(module)
try:
domain_id = None
if domain:
domain_id = _get_domain_id(cloud, domain)
user = cloud.get_user(name, domain_id=domain_id)
domain_id = self._get_domain_id(domain)
user = self.conn.get_user(name, domain_id=domain_id)
else:
user = cloud.get_user(name)
user = self.conn.get_user(name)
if state == 'present':
if update_password in ('always', 'on_create'):
if not password:
msg = "update_password is %s but a password value is missing" % update_password
module.fail_json(msg=msg)
self.fail_json(msg=msg)
default_project_id = None
if default_project:
default_project_id = _get_default_project_id(cloud, default_project, domain_id, module)
default_project_id = self._get_default_project_id(
default_project, domain_id)
if user is None:
if description is not None:
user = cloud.create_user(
user = self.conn.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled, description=description)
else:
user = cloud.create_user(
user = self.conn.create_user(
name=name, password=password, email=email,
default_project=default_project_id, domain_id=domain_id,
enabled=enabled)
@@ -246,47 +238,49 @@ def main():
if default_project_id is not None:
params_dict['default_project_id'] = default_project_id
if _needs_update(params_dict, user):
if self._needs_update(params_dict, user):
if update_password == 'always':
if description is not None:
user = cloud.update_user(
user = self.conn.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = cloud.update_user(
user = self.conn.update_user(
user['id'], password=password, email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
else:
if description is not None:
user = cloud.update_user(
user = self.conn.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled, description=description)
else:
user = cloud.update_user(
user = self.conn.update_user(
user['id'], email=email,
default_project=default_project_id,
domain_id=domain_id, enabled=enabled)
changed = True
else:
changed = False
module.exit_json(changed=changed, user=user)
self.exit_json(changed=changed, user=user)
elif state == 'absent':
if user is None:
changed = False
else:
if domain:
cloud.delete_user(user['id'], domain_id=domain_id)
self.conn.delete_user(user['id'], domain_id=domain_id)
else:
cloud.delete_user(user['id'])
self.conn.delete_user(user['id'])
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
def main():
module = IdentityUserModule()
module()
if __name__ == '__main__':

View File

@@ -107,61 +107,49 @@ openstack_users:
type: str
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_full_argument_spec,
openstack_cloud_from_module,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class IdentityUserInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name == 'openstack.cloud.identity_user_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.identity_user_facts' module has been renamed to 'openstack.cloud.identity_user_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
deprecated_names = ('openstack.cloud.identity_user_facts')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
def run(self):
name = self.params['name']
domain = self.params['domain']
filters = self.params['filters']
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
dom = self.conn.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
self.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
filters['domain_id'] = domain
users = opcloud.search_users(name, filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_users=users))
else:
module.exit_json(changed=False, openstack_users=users)
users = self.conn.search_users(name, filters)
self.exit_json(changed=False, openstack_users=users)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityUserInfoModule()
module()
if __name__ == '__main__':

View File

@@ -85,6 +85,12 @@ options:
choices: [present, absent]
default: present
type: str
tags:
description:
- List of tags to be applied to the image
default: []
type: list
elements: str
volume:
description:
- ID of a volume to create an image from.
@@ -115,6 +121,8 @@ EXAMPLES = '''
filename: cirros-0.3.0-x86_64-disk.img
kernel: cirros-vmlinuz
ramdisk: cirros-initrd
tags:
- custom
properties:
cpu_arch: x86_64
distro: ubuntu
@@ -147,95 +155,99 @@ EXAMPLES = '''
name: myvol_image
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
class ImageModule(OpenStackModule):
argument_spec = openstack_full_argument_spec(
name=dict(required=True),
id=dict(default=None),
checksum=dict(default=None),
disk_format=dict(default='qcow2', choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
deprecated_names = ('os_image', 'openstack.cloud.os_image')
argument_spec = dict(
name=dict(required=True, type='str'),
id=dict(type='str'),
checksum=dict(type='str'),
disk_format=dict(default='qcow2',
choices=['ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso', 'vhdx', 'ploop']),
container_format=dict(default='bare', choices=['ami', 'aki', 'ari', 'bare', 'ovf', 'ova', 'docker']),
owner=dict(default=None),
owner=dict(type='str'),
min_disk=dict(type='int', default=0),
min_ram=dict(type='int', default=0),
is_public=dict(type='bool', default=False),
protected=dict(type='bool', default=False),
filename=dict(default=None),
ramdisk=dict(default=None),
kernel=dict(default=None),
filename=dict(type='str'),
ramdisk=dict(type='str'),
kernel=dict(type='str'),
properties=dict(type='dict', default={}),
volume=dict(default=None),
volume=dict(type='str'),
tags=dict(type='list', default=[], elements='str'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[['filename', 'volume']],
)
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:
def run(self):
changed = False
if module.params['id']:
image = cloud.get_image(name_or_id=module.params['id'])
elif module.params['checksum']:
image = cloud.get_image(name_or_id=module.params['name'], filters={'checksum': module.params['checksum']})
if self.params['id']:
image = self.conn.get_image(name_or_id=self.params['id'])
elif self.params['checksum']:
image = self.conn.get_image(name_or_id=self.params['name'], filters={'checksum': self.params['checksum']})
else:
image = cloud.get_image(name_or_id=module.params['name'])
image = self.conn.get_image(name_or_id=self.params['name'])
if module.params['state'] == 'present':
if self.params['state'] == 'present':
if not image:
kwargs = {}
if module.params['id'] is not None:
kwargs['id'] = module.params['id']
image = cloud.create_image(
name=module.params['name'],
filename=module.params['filename'],
disk_format=module.params['disk_format'],
container_format=module.params['container_format'],
wait=module.params['wait'],
timeout=module.params['timeout'],
is_public=module.params['is_public'],
protected=module.params['protected'],
min_disk=module.params['min_disk'],
min_ram=module.params['min_ram'],
volume=module.params['volume'],
if self.params['id'] is not None:
kwargs['id'] = self.params['id']
image = self.conn.create_image(
name=self.params['name'],
filename=self.params['filename'],
disk_format=self.params['disk_format'],
container_format=self.params['container_format'],
wait=self.params['wait'],
timeout=self.params['timeout'],
is_public=self.params['is_public'],
protected=self.params['protected'],
min_disk=self.params['min_disk'],
min_ram=self.params['min_ram'],
volume=self.params['volume'],
tags=self.params['tags'],
**kwargs
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed, image=image, id=image.id)
if not self.params['wait']:
self.exit(changed=changed, image=image, id=image.id)
cloud.update_image_properties(
self.conn.update_image_properties(
image=image,
kernel=module.params['kernel'],
ramdisk=module.params['ramdisk'],
protected=module.params['protected'],
**module.params['properties'])
image = cloud.get_image(name_or_id=image.id)
module.exit_json(changed=changed, image=image, id=image.id)
kernel=self.params['kernel'],
ramdisk=self.params['ramdisk'],
protected=self.params['protected'],
**self.params['properties'])
if self.params['tags']:
self.conn.image.update_image(image.id, tags=self.params['tags'])
image = self.conn.get_image(name_or_id=image.id)
self.exit(changed=changed, image=image, id=image.id)
elif module.params['state'] == 'absent':
elif self.params['state'] == 'absent':
if not image:
changed = False
else:
cloud.delete_image(
name_or_id=module.params['name'],
wait=module.params['wait'],
timeout=module.params['timeout'])
self.conn.delete_image(
name_or_id=self.params['name'],
wait=self.params['wait'],
timeout=self.params['timeout'])
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.exit(changed=changed)
if __name__ == "__main__":
def main():
module = ImageModule()
module()
if __name__ == '__main__':
main()

View File

@@ -140,46 +140,37 @@ openstack_image:
description: Size of the image.
returned: success
type: int
tags:
description: List of tags assigned to the image
returned: success
type: list
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class ImageInfoModule(OpenStackModule):
deprecated_names = ('openstack.cloud.os_image_facts', 'openstack.cloud.os_image_info')
argument_spec = dict(
image=dict(type='str', required=False),
properties=dict(type='dict', required=False),
)
def run(self):
if self.params['image']:
image = self.conn.get_image(self.params['image'])
self.exit(changed=False, openstack_image=image)
else:
images = self.conn.search_images(filters=self.params['properties'])
self.exit(changed=False, openstack_image=images)
def main():
argument_spec = openstack_full_argument_spec(
image=dict(required=False),
properties=dict(default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.image_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.image_facts' module has been renamed to 'openstack.cloud.image_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['image']:
image = cloud.get_image(module.params['image'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=image))
else:
module.exit_json(changed=False, openstack_image=image)
else:
images = cloud.search_images(filters=module.params['properties'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=images))
else:
module.exit_json(changed=False, openstack_image=images)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
module = ImageInfoModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1,153 @@
#!/usr/bin/python
# Copyright (c) 2021 T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: keypair_info
short_description: Get information about keypairs from OpenStack
author: OpenStack Ansible SIG
description:
- Get information about keypairs that are associated with the account
options:
name:
description:
- Name or ID of the keypair
type: str
user_id:
description:
- It allows admin users to operate key-pairs of specified user ID.
type: str
limit:
description:
- Requests a page size of items.
- Returns a number of items up to a limit value.
type: int
marker:
description:
- The last-seen item.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Get information about keypairs
openstack.cloud.keypair_info:
register: result
- name: Get information about keypairs using optional parameters
openstack.cloud.keypair_info:
name: "test"
user_id: "fed75b36fd7a4078a769178d2b1bd844"
limit: 10
marker: "jdksl"
register: result
'''
RETURN = '''
openstack_keypairs:
description:
- Lists keypairs that are associated with the account.
type: complex
returned: always
contains:
created_at:
description:
- The date and time when the resource was created.
type: str
sample: "2021-01-19T14:52:07.261634"
id:
description:
- The id identifying the keypair
type: str
sample: "keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3"
is_deleted:
description:
- A boolean indicates whether this keypair is deleted or not.
type: bool
fingerprint:
description:
- The fingerprint for the keypair.
type: str
sample: "7e:eb:ab:24:ba:d1:e1:88:ae:9a:fb:66:53:df:d3:bd"
name:
description:
- A keypair name which will be used to reference it later.
type: str
sample: "keypair-5d935425-31d5-48a7-a0f1-e76e9813f2c3"
private_key:
description:
- The private key for the keypair.
type: str
sample: "MIICXAIBAAKBgQCqGKukO ... hZj6+H0qtjTkVxwTCpvKe4eCZ0FPq"
public_key:
description:
- The keypair public key.
type: str
sample: "ssh-rsa AAAAB3NzaC1yc ... 8rPsBUHNLQp Generated-by-Nova"
type:
description:
- The type of the keypair.
- Allowed values are ssh or x509.
type: str
sample: "ssh"
user_id:
description:
- It allows admin users to operate key-pairs of specified user ID.
type: str
sample: "59b10f2a2138428ea9358e10c7e44444"
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
OpenStackModule)
class KeyPairInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(type='str', required=False),
user_id=dict(type='str', required=False),
limit=dict(type='int', required=False),
marker=dict(type='str', required=False)
)
def run(self):
name = self.params['name']
user_id = self.params['user_id']
limit = self.params['limit']
marker = self.params['marker']
filters = {}
data = []
if user_id:
filters['user_id'] = user_id
if limit:
filters['limit'] = limit
if marker:
filters['marker'] = marker
result = self.conn.search_keypairs(name_or_id=name,
filters=filters)
raws = [raw if isinstance(raw, dict) else raw.to_dict()
for raw in result]
for raw in raws:
raw.pop('location')
data.append(raw)
self.exit(changed=False, openstack_keypairs=data)
def main():
module = KeyPairInfoModule()
module()
if __name__ == '__main__':
main()

View File

@@ -60,151 +60,127 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_protocol(protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
return None
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
def delete_protocol(module, sdk, cloud, protocol):
"""
Delete an existing Protocol
returns: the "Changed" state
"""
if protocol is None:
return False
if module.check_mode:
return True
try:
cloud.identity.delete_federation_protocol(None, protocol)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to delete protocol: {0}'.format(str(ex)))
return True
def create_protocol(module, sdk, cloud, name):
"""
Create a new Protocol
returns: the "Changed" state and the new protocol
"""
if module.check_mode:
return True, None
idp_name = module.params.get('idp_id')
mapping_id = module.params.get('mapping_id')
attributes = {
'idp_id': idp_name,
'mapping_id': mapping_id,
}
try:
protocol = cloud.identity.create_federation_protocol(id=name, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to create protocol: {0}'.format(str(ex)))
return (True, protocol)
def update_protocol(module, sdk, cloud, protocol):
"""
Update an existing Protocol
returns: the "Changed" state and the new protocol
"""
mapping_id = module.params.get('mapping_id')
attributes = {}
if (mapping_id is not None) and (mapping_id != protocol.mapping_id):
attributes['mapping_id'] = mapping_id
if not attributes:
return False, protocol
if module.check_mode:
return True, None
try:
new_protocol = cloud.identity.update_federation_protocol(None, protocol, **attributes)
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to update protocol: {0}'.format(str(ex)))
return (True, new_protocol)
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
class IdentityFederationProtocolModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True, aliases=['id']),
state=dict(default='present', choices=['absent', 'present']),
idp_id=dict(required=True, aliases=['idp_name']),
mapping_id=dict(aliases=['mapping_name']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
module_kwargs = dict(
supports_check_mode=True
)
name = module.params.get('name')
state = module.params.get('state')
idp = module.params.get('idp_id')
changed = False
def normalize_protocol(self, protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
try:
protocol = cloud.identity.get_federation_protocol(idp, name)
except sdk.exceptions.ResourceNotFound:
protocol = None
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
if state == 'absent':
if protocol is not None:
changed = delete_protocol(module, sdk, cloud, protocol)
module.exit_json(changed=changed)
# state == 'present'
else:
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
if module.params.get('mapping_id') is None:
module.fail_json(msg='A mapping_id must be passed when creating'
' a protocol')
(changed, protocol) = create_protocol(module, sdk, cloud, name)
protocol = normalize_protocol(protocol)
module.exit_json(changed=changed, protocol=protocol)
return None
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
def delete_protocol(self, protocol):
"""
Delete an existing Protocol
returns: the "Changed" state
"""
if protocol is None:
return False
if self.ansible.check_mode:
return True
self.conn.identity.delete_federation_protocol(None, protocol)
return True
def create_protocol(self, name):
"""
Create a new Protocol
returns: the "Changed" state and the new protocol
"""
if self.ansible.check_mode:
return True, None
idp_name = self.params.get('idp_id')
mapping_id = self.params.get('mapping_id')
attributes = {
'idp_id': idp_name,
'mapping_id': mapping_id,
}
protocol = self.conn.identity.create_federation_protocol(id=name, **attributes)
return (True, protocol)
def update_protocol(self, protocol):
"""
Update an existing Protocol
returns: the "Changed" state and the new protocol
"""
mapping_id = self.params.get('mapping_id')
attributes = {}
if (mapping_id is not None) and (mapping_id != protocol.mapping_id):
attributes['mapping_id'] = mapping_id
if not attributes:
return False, protocol
if self.ansible.check_mode:
return True, None
new_protocol = self.conn.identity.update_federation_protocol(None, protocol, **attributes)
return (True, new_protocol)
def run(self):
""" Module entry point """
name = self.params.get('name')
state = self.params.get('state')
idp = self.params.get('idp_id')
changed = False
protocol = self.conn.identity.find_federation_protocol(idp, name)
if state == 'absent':
if protocol is not None:
changed = self.delete_protocol(protocol)
self.exit_json(changed=changed)
# state == 'present'
else:
(changed, new_protocol) = update_protocol(module, sdk, cloud, protocol)
new_protocol = normalize_protocol(new_protocol)
module.exit_json(changed=changed, protocol=new_protocol)
if protocol is None:
if self.params.get('mapping_id') is None:
self.fail_json(
msg='A mapping_id must be passed when creating'
' a protocol')
(changed, protocol) = self.create_protocol(name)
protocol = self.normalize_protocol(protocol)
self.exit_json(changed=changed, protocol=protocol)
else:
(changed, new_protocol) = self.update_protocol(protocol)
new_protocol = self.normalize_protocol(new_protocol)
self.exit_json(changed=changed, protocol=new_protocol)
def main():
module = IdentityFederationProtocolModule()
module()
if __name__ == '__main__':

View File

@@ -45,65 +45,53 @@ EXAMPLES = '''
RETURN = '''
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_module_kwargs
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def normalize_protocol(protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
return None
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
def main():
""" Module entry point """
argument_spec = openstack_full_argument_spec(
class IdentityFederationProtocolInfoModule(OpenStackModule):
argument_spec = dict(
name=dict(aliases=['id']),
idp_id=dict(required=True, aliases=['idp_name']),
)
module_kwargs = openstack_module_kwargs(
)
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
module_kwargs = dict(
supports_check_mode=True
)
name = module.params.get('name')
idp = module.params.get('idp_id')
def normalize_protocol(self, protocol):
"""
Normalizes the protocol definitions so that the outputs are consistent with the
parameters
sdk, cloud = openstack_cloud_from_module(module, min_version="0.44")
- "name" (parameter) == "id" (SDK)
"""
if protocol is None:
return None
if name:
try:
protocol = cloud.identity.get_federation_protocol(idp, name)
protocol = normalize_protocol(protocol)
except sdk.exceptions.ResourceNotFound:
module.fail_json(msg='Failed to find protocol')
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to get protocol: {0}'.format(str(ex)))
module.exit_json(changed=False, protocols=[protocol])
_protocol = protocol.to_dict()
_protocol['name'] = protocol['id']
# As of 0.44 SDK doesn't copy the URI parameters over, so let's add them
_protocol['idp_id'] = protocol['idp_id']
return _protocol
else:
try:
protocols = list(map(normalize_protocol, cloud.identity.federation_protocols(idp)))
except sdk.exceptions.OpenStackCloudException as ex:
module.fail_json(msg='Failed to list protocols: {0}'.format(str(ex)))
module.exit_json(changed=False, protocols=protocols)
def run(self):
""" Module entry point """
name = self.params.get('name')
idp = self.params.get('idp_id')
if name:
protocol = self.conn.identity.get_federation_protocol(idp, name)
protocol = self.normalize_protocol(protocol)
self.exit_json(changed=False, protocols=[protocol])
else:
protocols = list(map(self.normalize_protocol, self.conn.identity.federation_protocols(idp)))
self.exit_json(changed=False, protocols=protocols)
def main():
module = IdentityFederationProtocolInfoModule()
module()
if __name__ == '__main__':

View File

@@ -30,7 +30,7 @@ options:
protocol:
description:
- The protocol for the listener.
choices: [HTTP, HTTPS, TCP, TERMINATED_HTTPS]
choices: [HTTP, HTTPS, TCP, TERMINATED_HTTPS, UDP, SCTP]
default: HTTP
type: str
protocol_port:
@@ -143,112 +143,110 @@ EXAMPLES = '''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _lb_wait_for_status(module, cloud, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
lb = cloud.load_balancer.get_load_balancer(lb.id)
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
module.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
class LoadbalancerListenerModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
loadbalancer=dict(required=True),
protocol=dict(default='HTTP',
choices=['HTTP', 'HTTPS', 'TCP', 'TERMINATED_HTTPS']),
choices=['HTTP', 'HTTPS', 'TCP', 'TERMINATED_HTTPS', 'UDP', 'SCTP']),
protocol_port=dict(default=80, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
loadbalancer = module.params['loadbalancer']
loadbalancer_id = None
module_kwargs = dict()
def _lb_wait_for_status(self, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = self.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
lb = self.conn.load_balancer.get_load_balancer(lb.id)
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
self.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
time.sleep(interval)
total_sleep += interval
self.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
def run(self):
loadbalancer = self.params['loadbalancer']
loadbalancer_id = None
try:
changed = False
listener = cloud.load_balancer.find_listener(
name_or_id=module.params['name'])
listener = self.conn.load_balancer.find_listener(
name_or_id=self.params['name'])
if module.params['state'] == 'present':
if self.params['state'] == 'present':
if not listener:
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
self.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
loadbalancer_id = lb.id
listener = cloud.load_balancer.create_listener(
name=module.params['name'],
listener = self.conn.load_balancer.create_listener(
name=self.params['name'],
loadbalancer_id=loadbalancer_id,
protocol=module.params['protocol'],
protocol_port=module.params['protocol_port'],
protocol=self.params['protocol'],
protocol_port=self.params['protocol_port'],
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
listener=listener.to_dict(),
id=listener.id)
if not self.params['wait']:
self.exit_json(
changed=changed, listener=listener.to_dict(),
id=listener.id)
if module.params['wait']:
if self.params['wait']:
# Check in case the listener already exists.
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
self.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
self._lb_wait_for_status(lb, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed, listener=listener.to_dict(),
id=listener.id)
elif module.params['state'] == 'absent':
self.exit_json(
changed=changed, listener=listener.to_dict(), id=listener.id)
elif self.params['state'] == 'absent':
if not listener:
changed = False
else:
cloud.load_balancer.delete_listener(listener)
self.conn.load_balancer.delete_listener(listener)
changed = True
if module.params['wait']:
if self.params['wait']:
# Wait for the load balancer to be active after deleting
# the listener.
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(
self.fail_json(
msg='load balancer %s is not found' % loadbalancer
)
_lb_wait_for_status(module, cloud, lb, "ACTIVE", ["ERROR"])
self._lb_wait_for_status(lb, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.exit_json(changed=changed)
def main():
module = LoadbalancerListenerModule()
module()
if __name__ == "__main__":

View File

@@ -130,42 +130,11 @@ EXAMPLES = '''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _wait_for_member_status(module, cloud, pool_id, member_id, status,
failures, interval=5):
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
member = cloud.load_balancer.get_member(member_id, pool_id)
provisioning_status = member.provisioning_status
if provisioning_status == status:
return member
if provisioning_status in failures:
module.fail_json(
msg="Member %s transitioned to failure state %s" %
(member_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for member %s to transition to %s" %
(member_id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
class LoadbalancerMemberModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
pool=dict(required=True),
@@ -173,54 +142,81 @@ def main():
protocol_port=dict(default=80, type='int'),
subnet_id=dict(default=None),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
name = module.params['name']
pool = module.params['pool']
module_kwargs = dict()
def _wait_for_member_status(self, pool_id, member_id, status,
failures, interval=5):
timeout = self.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
member = self.conn.load_balancer.get_member(member_id, pool_id)
provisioning_status = member.provisioning_status
if provisioning_status == status:
return member
if provisioning_status in failures:
self.fail_json(
msg="Member %s transitioned to failure state %s" %
(member_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
self.fail_json(
msg="Timeout waiting for member %s to transition to %s" %
(member_id, status)
)
def run(self):
name = self.params['name']
pool = self.params['pool']
try:
changed = False
pool_ret = cloud.load_balancer.find_pool(name_or_id=pool)
pool_ret = self.conn.load_balancer.find_pool(name_or_id=pool)
if not pool_ret:
module.fail_json(msg='pool %s is not found' % pool)
self.fail_json(msg='pool %s is not found' % pool)
pool_id = pool_ret.id
member = cloud.load_balancer.find_member(name, pool_id)
member = self.conn.load_balancer.find_member(name, pool_id)
if module.params['state'] == 'present':
if self.params['state'] == 'present':
if not member:
member = cloud.load_balancer.create_member(
member = self.conn.load_balancer.create_member(
pool_ret,
address=module.params['address'],
address=self.params['address'],
name=name,
protocol_port=module.params['protocol_port'],
subnet_id=module.params['subnet_id']
protocol_port=self.params['protocol_port'],
subnet_id=self.params['subnet_id']
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
member=member.to_dict(),
id=member.id)
if not self.params['wait']:
self.exit_json(
changed=changed, member=member.to_dict(), id=member.id)
if module.params['wait']:
member = _wait_for_member_status(module, cloud, pool_id,
member.id, "ACTIVE",
["ERROR"])
if self.params['wait']:
member = self._wait_for_member_status(
pool_id, member.id, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed, member=member.to_dict(),
id=member.id)
self.exit_json(
changed=changed, member=member.to_dict(), id=member.id)
elif module.params['state'] == 'absent':
elif self.params['state'] == 'absent':
if member:
cloud.load_balancer.delete_member(member, pool_ret)
self.conn.load_balancer.delete_member(member, pool_ret)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.exit_json(changed=changed)
def main():
module = LoadbalancerMemberModule()
module()
if __name__ == "__main__":

View File

@@ -149,41 +149,11 @@ EXAMPLES = '''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, \
openstack_module_kwargs, openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _wait_for_pool_status(module, cloud, pool_id, status, failures,
interval=5):
timeout = module.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
pool = cloud.load_balancer.get_pool(pool_id)
provisioning_status = pool.provisioning_status
if provisioning_status == status:
return pool
if provisioning_status in failures:
module.fail_json(
msg="pool %s transitioned to failure state %s" %
(pool_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="timeout waiting for pool %s to transition to %s" %
(pool_id, status)
)
def main():
argument_spec = openstack_full_argument_spec(
class LoadbalancerPoolModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
loadbalancer=dict(default=None),
@@ -195,71 +165,98 @@ def main():
choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP']
)
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[['loadbalancer', 'listener']]
)
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
loadbalancer = module.params['loadbalancer']
listener = module.params['listener']
def _wait_for_pool_status(self, pool_id, status, failures,
interval=5):
timeout = self.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
pool = self.conn.load_balancer.get_pool(pool_id)
provisioning_status = pool.provisioning_status
if provisioning_status == status:
return pool
if provisioning_status in failures:
self.fail_json(
msg="pool %s transitioned to failure state %s" %
(pool_id, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
self.fail_json(
msg="timeout waiting for pool %s to transition to %s" %
(pool_id, status)
)
def run(self):
loadbalancer = self.params['loadbalancer']
listener = self.params['listener']
try:
changed = False
pool = cloud.load_balancer.find_pool(name_or_id=module.params['name'])
pool = self.conn.load_balancer.find_pool(name_or_id=self.params['name'])
if module.params['state'] == 'present':
if self.params['state'] == 'present':
if not pool:
loadbalancer_id = None
if not (loadbalancer or listener):
module.fail_json(
self.fail_json(
msg="either loadbalancer or listener must be provided"
)
if loadbalancer:
lb = cloud.load_balancer.find_load_balancer(loadbalancer)
lb = self.conn.load_balancer.find_load_balancer(loadbalancer)
if not lb:
module.fail_json(msg='load balancer %s is not '
'found' % loadbalancer)
self.fail_json(
msg='load balancer %s is not found' % loadbalancer)
loadbalancer_id = lb.id
listener_id = None
if listener:
listener_ret = cloud.load_balancer.find_listener(listener)
listener_ret = self.conn.load_balancer.find_listener(listener)
if not listener_ret:
module.fail_json(msg='listener %s is not found'
% listener)
self.fail_json(
msg='listener %s is not found' % listener)
listener_id = listener_ret.id
pool = cloud.load_balancer.create_pool(
name=module.params['name'],
pool = self.conn.load_balancer.create_pool(
name=self.params['name'],
loadbalancer_id=loadbalancer_id,
listener_id=listener_id,
protocol=module.params['protocol'],
lb_algorithm=module.params['lb_algorithm']
protocol=self.params['protocol'],
lb_algorithm=self.params['lb_algorithm']
)
changed = True
if not module.params['wait']:
module.exit_json(changed=changed,
pool=pool.to_dict(),
id=pool.id)
if not self.params['wait']:
self.exit_json(
changed=changed, pool=pool.to_dict(), id=pool.id)
if module.params['wait']:
pool = _wait_for_pool_status(module, cloud, pool.id, "ACTIVE",
["ERROR"])
if self.params['wait']:
pool = self._wait_for_pool_status(
pool.id, "ACTIVE", ["ERROR"])
module.exit_json(changed=changed, pool=pool.to_dict(),
id=pool.id)
self.exit_json(
changed=changed, pool=pool.to_dict(), id=pool.id)
elif module.params['state'] == 'absent':
elif self.params['state'] == 'absent':
if pool:
cloud.load_balancer.delete_pool(pool)
self.conn.load_balancer.delete_pool(pool)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.exit_json(changed=changed)
def main():
module = LoadbalancerPoolModule()
module()
if __name__ == "__main__":

View File

@@ -83,6 +83,12 @@ options:
description:
- The protocol port number for the listener.
default: 80
allowed_cidrs:
description:
- A list of IPv4, IPv6 or mix of both CIDRs to be allowed access to the listener. The default is all allowed.
When a list of CIDRs is provided, the default switches to deny all.
Ignored on unsupported Octavia versions (less than 2.12)
default: []
pool:
description:
- The pool attached to the listener.
@@ -285,51 +291,76 @@ EXAMPLES = '''
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _wait_for_lb(module, cloud, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = module.params['timeout']
class LoadBalancerModule(OpenStackModule):
total_sleep = 0
if failures is None:
failures = []
def _wait_for_pool(self, pool, provisioning_status, operating_status, failures, interval=5):
"""Wait for pool to be in a particular provisioning and operating status."""
timeout = self.params['timeout'] # reuse loadbalancer timeout
while total_sleep < timeout:
lb = cloud.load_balancer.find_load_balancer(lb.id)
total_sleep = 0
if failures is None:
failures = []
if lb:
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
module.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
else:
if status == "DELETED":
return None
while total_sleep < timeout:
pool = self.conn.load_balancer.find_pool(name_or_id=pool.id)
if pool:
if pool.provisioning_status == provisioning_status and pool.operating_status == operating_status:
return None
if pool.provisioning_status in failures:
self.fail_json(
msg="Pool %s transitioned to failure state %s" %
(pool.id, pool.provisioning_status)
)
else:
module.fail_json(
msg="Load Balancer %s transitioned to DELETED" % lb.id
)
if provisioning_status == "DELETED":
return None
else:
self.fail_json(
msg="Pool %s transitioned to DELETED" % pool.id
)
time.sleep(interval)
total_sleep += interval
time.sleep(interval)
total_sleep += interval
module.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
def _wait_for_lb(self, lb, status, failures, interval=5):
"""Wait for load balancer to be in a particular provisioning status."""
timeout = self.params['timeout']
total_sleep = 0
if failures is None:
failures = []
def main():
argument_spec = openstack_full_argument_spec(
while total_sleep < timeout:
lb = self.conn.load_balancer.find_load_balancer(lb.id)
if lb:
if lb.provisioning_status == status:
return None
if lb.provisioning_status in failures:
self.fail_json(
msg="Load Balancer %s transitioned to failure state %s" %
(lb.id, lb.provisioning_status)
)
else:
if status == "DELETED":
return None
else:
self.fail_json(
msg="Load Balancer %s transitioned to DELETED" % lb.id
)
time.sleep(interval)
total_sleep += interval
self.fail_json(
msg="Timeout waiting for Load Balancer %s to transition to %s" %
(lb.id, status)
)
argument_spec = dict(
name=dict(required=True),
flavor=dict(required=False),
state=dict(default='present', choices=['absent', 'present']),
@@ -343,162 +374,186 @@ def main():
public_network=dict(required=False),
delete_public_ip=dict(required=False, default=False, type='bool'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
module_kwargs = dict(supports_check_mode=True)
flavor = module.params['flavor']
vip_network = module.params['vip_network']
vip_subnet = module.params['vip_subnet']
vip_port = module.params['vip_port']
listeners = module.params['listeners']
public_vip_address = module.params['public_ip_address']
allocate_fip = module.params['auto_public_ip']
delete_fip = module.params['delete_public_ip']
public_network = module.params['public_network']
def run(self):
flavor = self.params['flavor']
vip_network = self.params['vip_network']
vip_subnet = self.params['vip_subnet']
vip_port = self.params['vip_port']
listeners = self.params['listeners']
public_vip_address = self.params['public_ip_address']
allocate_fip = self.params['auto_public_ip']
delete_fip = self.params['delete_public_ip']
public_network = self.params['public_network']
vip_network_id = None
vip_subnet_id = None
vip_port_id = None
flavor_id = None
vip_network_id = None
vip_subnet_id = None
vip_port_id = None
flavor_id = None
try:
changed = False
lb = cloud.load_balancer.find_load_balancer(
name_or_id=module.params['name'])
try:
max_microversion = 1
max_majorversion = 2
changed = False
lb = self.conn.load_balancer.find_load_balancer(
name_or_id=self.params['name'])
if module.params['state'] == 'present':
if not lb:
if not (vip_network or vip_subnet or vip_port):
module.fail_json(
msg="One of vip_network, vip_subnet, or vip_port must "
"be specified for load balancer creation"
)
if self.params['state'] == 'present':
if lb and self.ansible.check_mode:
self.exit_json(changed=False)
if lb:
self.exit_json(changed=False)
ver_data = self.conn.load_balancer.get_all_version_data()
region = list(ver_data.keys())[0]
interface_type = list(ver_data[region].keys())[0]
versions = ver_data[region][interface_type]['load-balancer']
for ver in versions:
if ver['status'] == 'CURRENT':
curversion = ver['version'].split(".")
max_majorversion = int(curversion[0])
max_microversion = int(curversion[1])
if flavor:
_flavor = cloud.load_balancer.find_flavor(flavor)
if not _flavor:
module.fail_json(
msg='flavor %s not found' % flavor
if not lb:
if self.ansible.check_mode:
self.exit_json(changed=True)
if not (vip_network or vip_subnet or vip_port):
self.fail_json(
msg="One of vip_network, vip_subnet, or vip_port must "
"be specified for load balancer creation"
)
flavor_id = _flavor.id
if vip_network:
network = cloud.get_network(vip_network)
if not network:
module.fail_json(
msg='network %s is not found' % vip_network
)
vip_network_id = network.id
if vip_subnet:
subnet = cloud.get_subnet(vip_subnet)
if not subnet:
module.fail_json(
msg='subnet %s is not found' % vip_subnet
)
vip_subnet_id = subnet.id
if vip_port:
port = cloud.get_port(vip_port)
if not port:
module.fail_json(
msg='port %s is not found' % vip_port
)
vip_port_id = port.id
if flavor:
_flavor = self.conn.load_balancer.find_flavor(flavor)
if not _flavor:
self.fail_json(
msg='flavor %s not found' % flavor
)
flavor_id = _flavor.id
lb = cloud.load_balancer.create_load_balancer(
name=module.params['name'],
flavor_id=flavor_id,
vip_network_id=vip_network_id,
vip_subnet_id=vip_subnet_id,
vip_port_id=vip_port_id,
vip_address=module.params['vip_address'],
)
changed = True
if vip_network:
network = self.conn.get_network(vip_network)
if not network:
self.fail_json(
msg='network %s is not found' % vip_network
)
vip_network_id = network.id
if vip_subnet:
subnet = self.conn.get_subnet(vip_subnet)
if not subnet:
self.fail_json(
msg='subnet %s is not found' % vip_subnet
)
vip_subnet_id = subnet.id
if vip_port:
port = self.conn.get_port(vip_port)
if not listeners and not module.params['wait']:
module.exit_json(
changed=changed,
loadbalancer=lb.to_dict(),
id=lb.id
)
if not port:
self.fail_json(
msg='port %s is not found' % vip_port
)
vip_port_id = port.id
lbargs = {"name": self.params['name'],
"vip_network_id": vip_network_id,
"vip_subnet_id": vip_subnet_id,
"vip_port_id": vip_port_id,
"vip_address": self.params['vip_address']
}
if flavor_id is not None:
lbargs["flavor_id"] = flavor_id
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
lb = self.conn.load_balancer.create_load_balancer(**lbargs)
for listener_def in listeners:
listener_name = listener_def.get("name")
pool_def = listener_def.get("pool")
if not listener_name:
module.fail_json(msg='listener name is required')
listener = cloud.load_balancer.find_listener(
name_or_id=listener_name
)
if not listener:
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
protocol = listener_def.get("protocol", "HTTP")
protocol_port = listener_def.get("protocol_port", 80)
listener = cloud.load_balancer.create_listener(
name=listener_name,
loadbalancer_id=lb.id,
protocol=protocol,
protocol_port=protocol_port,
)
changed = True
# Ensure pool in the listener.
if pool_def:
pool_name = pool_def.get("name")
members = pool_def.get('members', [])
if not listeners and not self.params['wait']:
self.exit_json(
changed=changed,
loadbalancer=lb.to_dict(),
id=lb.id
)
if not pool_name:
module.fail_json(msg='pool name is required')
self._wait_for_lb(lb, "ACTIVE", ["ERROR"])
pool = cloud.load_balancer.find_pool(name_or_id=pool_name)
for listener_def in listeners:
listener_name = listener_def.get("name")
pool_def = listener_def.get("pool")
if not pool:
_wait_for_lb(module, cloud, lb, "ACTIVE", ["ERROR"])
if not listener_name:
self.fail_json(msg='listener name is required')
protocol = pool_def.get("protocol", "HTTP")
lb_algorithm = pool_def.get("lb_algorithm",
"ROUND_ROBIN")
listener = self.conn.load_balancer.find_listener(
name_or_id=listener_name
)
pool = cloud.load_balancer.create_pool(
name=pool_name,
listener_id=listener.id,
protocol=protocol,
lb_algorithm=lb_algorithm
)
if not listener:
self._wait_for_lb(lb, "ACTIVE", ["ERROR"])
protocol = listener_def.get("protocol", "HTTP")
protocol_port = listener_def.get("protocol_port", 80)
allowed_cidrs = listener_def.get("allowed_cidrs", [])
listenerargs = {"name": listener_name,
"loadbalancer_id": lb.id,
"protocol": protocol,
"protocol_port": protocol_port
}
if max_microversion >= 12 and max_majorversion >= 2:
listenerargs['allowed_cidrs'] = allowed_cidrs
listener = self.conn.load_balancer.create_listener(**listenerargs)
changed = True
# Ensure pool in the listener.
if pool_def:
pool_name = pool_def.get("name")
members = pool_def.get('members', [])
if not pool_name:
self.fail_json(msg='pool name is required')
pool = self.conn.load_balancer.find_pool(name_or_id=pool_name)
if not pool:
self._wait_for_lb(lb, "ACTIVE", ["ERROR"])
protocol = pool_def.get("protocol", "HTTP")
lb_algorithm = pool_def.get("lb_algorithm",
"ROUND_ROBIN")
pool = self.conn.load_balancer.create_pool(
name=pool_name,
listener_id=listener.id,
protocol=protocol,
lb_algorithm=lb_algorithm
)
self._wait_for_pool(pool, "ACTIVE", "ONLINE", ["ERROR"])
changed = True
# Ensure members in the pool
for member_def in members:
member_name = member_def.get("name")
if not member_name:
module.fail_json(msg='member name is required')
for member_def in members:
member_name = member_def.get("name")
if not member_name:
self.fail_json(msg='member name is required')
member = cloud.load_balancer.find_member(member_name,
pool.id)
member = self.conn.load_balancer.find_member(member_name,
pool.id
)
if not member:
_wait_for_lb(module, cloud, lb, "ACTIVE",
["ERROR"])
if not member:
self._wait_for_lb(lb, "ACTIVE", ["ERROR"])
address = member_def.get("address")
if not address:
module.fail_json(
self.fail_json(
msg='member address for member %s is '
'required' % member_name
)
subnet_id = member_def.get("subnet")
if subnet_id:
subnet = cloud.get_subnet(subnet_id)
subnet = self.conn.get_subnet(subnet_id)
if not subnet:
module.fail_json(
self.fail_json(
msg='subnet %s for member %s is not '
'found' % (subnet_id, member_name)
)
@@ -506,119 +561,130 @@ def main():
protocol_port = member_def.get("protocol_port", 80)
member = cloud.load_balancer.create_member(
member = self.conn.load_balancer.create_member(
pool,
name=member_name,
address=address,
protocol_port=protocol_port,
subnet_id=subnet_id
)
self._wait_for_pool(pool, "ACTIVE", "ONLINE", ["ERROR"])
changed = True
# Associate public ip to the load balancer VIP. If
# public_vip_address is provided, use that IP, otherwise, either
# find an available public ip or create a new one.
fip = None
orig_public_ip = None
new_public_ip = None
if public_vip_address or allocate_fip:
ips = cloud.network.ips(
port_id=lb.vip_port_id,
fixed_ip_address=lb.vip_address
)
ips = list(ips)
if ips:
orig_public_ip = ips[0]
new_public_ip = orig_public_ip.floating_ip_address
if public_vip_address and public_vip_address != orig_public_ip:
fip = cloud.network.find_ip(public_vip_address)
if not fip:
module.fail_json(
msg='Public IP %s is unavailable' % public_vip_address
)
# Release origin public ip first
cloud.network.update_ip(
orig_public_ip,
fixed_ip_address=None,
port_id=None
)
# Associate new public ip
cloud.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = public_vip_address
changed = True
elif allocate_fip and not orig_public_ip:
fip = cloud.network.find_available_ip()
if not fip:
if not public_network:
module.fail_json(msg="Public network is not provided")
pub_net = cloud.network.find_network(public_network)
if not pub_net:
module.fail_json(
msg='Public network %s not found' %
public_network
)
fip = cloud.network.create_ip(
floating_network_id=pub_net.id
)
cloud.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = fip.floating_ip_address
changed = True
# Include public_vip_address in the result.
lb = cloud.load_balancer.find_load_balancer(name_or_id=lb.id)
lb_dict = lb.to_dict()
lb_dict.update({"public_vip_address": new_public_ip})
module.exit_json(
changed=changed,
loadbalancer=lb_dict,
id=lb.id
)
elif module.params['state'] == 'absent':
changed = False
public_vip_address = None
if lb:
if delete_fip:
ips = cloud.network.ips(
# Associate public ip to the load balancer VIP. If
# public_vip_address is provided, use that IP, otherwise, either
# find an available public ip or create a new one.
fip = None
orig_public_ip = None
new_public_ip = None
if public_vip_address or allocate_fip:
ips = self.conn.network.ips(
port_id=lb.vip_port_id,
fixed_ip_address=lb.vip_address
)
ips = list(ips)
if ips:
public_vip_address = ips[0]
orig_public_ip = ips[0]
new_public_ip = orig_public_ip.floating_ip_address
# Deleting load balancer with `cascade=False` does not make
# sense because the deletion will always fail if there are
# sub-resources.
cloud.load_balancer.delete_load_balancer(lb, cascade=True)
changed = True
if public_vip_address and public_vip_address != orig_public_ip:
fip = self.conn.network.find_ip(public_vip_address)
if module.params['wait']:
_wait_for_lb(module, cloud, lb, "DELETED", ["ERROR"])
if not fip:
self.fail_json(
msg='Public IP %s is unavailable' % public_vip_address
)
if delete_fip and public_vip_address:
cloud.network.delete_ip(public_vip_address)
changed = True
# Release origin public ip first
self.conn.network.update_ip(
orig_public_ip,
fixed_ip_address=None,
port_id=None
)
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
# Associate new public ip
self.conn.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = public_vip_address
changed = True
elif allocate_fip and not orig_public_ip:
fip = self.conn.network.find_available_ip()
if not fip:
if not public_network:
self.fail_json(msg="Public network is not provided")
pub_net = self.conn.network.find_network(public_network)
if not pub_net:
self.fail_json(
msg='Public network %s not found' %
public_network
)
fip = self.conn.network.create_ip(
floating_network_id=pub_net.id
)
self.conn.network.update_ip(
fip,
fixed_ip_address=lb.vip_address,
port_id=lb.vip_port_id
)
new_public_ip = fip.floating_ip_address
changed = True
# Include public_vip_address in the result.
lb = self.conn.load_balancer.find_load_balancer(name_or_id=lb.id)
lb_dict = lb.to_dict()
lb_dict.update({"public_vip_address": new_public_ip})
self.exit_json(
changed=changed,
loadbalancer=lb_dict,
id=lb.id
)
elif self.params['state'] == 'absent':
changed = False
public_vip_address = None
if lb:
if self.ansible.check_mode:
self.exit_json(changed=True)
if delete_fip:
ips = self.conn.network.ips(
port_id=lb.vip_port_id,
fixed_ip_address=lb.vip_address
)
ips = list(ips)
if ips:
public_vip_address = ips[0]
# Deleting load balancer with `cascade=False` does not make
# sense because the deletion will always fail if there are
# sub-resources.
self.conn.load_balancer.delete_load_balancer(lb, cascade=True)
changed = True
if self.params['wait']:
self._wait_for_lb(lb, "DELETED", ["ERROR"])
if delete_fip and public_vip_address:
self.conn.network.delete_ip(public_vip_address)
changed = True
elif self.ansible.check_mode:
self.exit_json(changed=False)
self.exit_json(changed=changed)
except Exception as e:
self.fail_json(msg=str(e))
def main():
module = LoadBalancerModule()
module()
if __name__ == "__main__":

View File

@@ -63,12 +63,13 @@ options:
Network will use OpenStack defaults if this option is
not utilised. Requires openstacksdk>=0.18.
type: bool
mtu:
mtu_size:
description:
- The maximum transmission unit (MTU) value to address fragmentation.
Network will use OpenStack defaults if this option is
not provided. Requires openstacksdk>=0.18.
type: int
aliases: ['mtu']
dns_domain:
description:
- The DNS domain value to set. Requires openstacksdk>=0.29.
@@ -156,14 +157,12 @@ network:
sample: 101
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class NetworkModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
shared=dict(default=False, type='bool'),
admin_state_up=dict(default=True, type='bool'),
@@ -173,51 +172,38 @@ def main():
provider_segmentation_id=dict(required=False, type='int'),
state=dict(default='present', choices=['absent', 'present']),
project=dict(default=None),
port_security_enabled=dict(type='bool'),
mtu=dict(required=False, type='int'),
dns_domain=dict(required=False)
port_security_enabled=dict(type='bool', min_ver='0.18.0'),
mtu_size=dict(required=False, type='int', min_ver='0.18.0', aliases=['mtu']),
dns_domain=dict(required=False, min_ver='0.29.0')
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
def run(self):
state = module.params['state']
name = module.params['name']
shared = module.params['shared']
admin_state_up = module.params['admin_state_up']
external = module.params['external']
provider_physical_network = module.params['provider_physical_network']
provider_network_type = module.params['provider_network_type']
provider_segmentation_id = module.params['provider_segmentation_id']
project = module.params['project']
state = self.params['state']
name = self.params['name']
shared = self.params['shared']
admin_state_up = self.params['admin_state_up']
external = self.params['external']
provider_physical_network = self.params['provider_physical_network']
provider_network_type = self.params['provider_network_type']
provider_segmentation_id = self.params['provider_segmentation_id']
project = self.params['project']
net_create_kwargs = {}
min_version = None
kwargs = self.check_versioned(
mtu_size=self.params['mtu_size'], port_security_enabled=self.params['port_security_enabled'],
dns_domain=self.params['dns_domain']
)
if module.params['mtu'] is not None:
min_version = '0.18.0'
net_create_kwargs['mtu_size'] = module.params['mtu']
if module.params['port_security_enabled'] is not None:
min_version = '0.18.0'
net_create_kwargs['port_security_enabled'] = module.params['port_security_enabled']
if module.params['dns_domain'] is not None:
min_version = '0.29.0'
net_create_kwargs['dns_domain'] = module.params['dns_domain']
sdk, cloud = openstack_cloud_from_module(module, min_version)
try:
if project is not None:
proj = cloud.get_project(project)
proj = self.conn.get_project(project)
if proj is None:
module.fail_json(msg='Project %s could not be found' % project)
self.fail_json(msg='Project %s could not be found' % project)
project_id = proj['id']
filters = {'tenant_id': project_id}
else:
project_id = None
filters = None
net = cloud.get_network(name, filters=filters)
net = self.conn.get_network(name, filters=filters)
if state == 'present':
if not net:
@@ -230,28 +216,30 @@ def main():
provider['segmentation_id'] = provider_segmentation_id
if project_id is not None:
net = cloud.create_network(name, shared, admin_state_up,
external, provider, project_id,
**net_create_kwargs)
net = self.conn.create_network(name, shared, admin_state_up,
external, provider, project_id,
**kwargs)
else:
net = cloud.create_network(name, shared, admin_state_up,
external, provider,
**net_create_kwargs)
net = self.conn.create_network(name, shared, admin_state_up,
external, provider,
**kwargs)
changed = True
else:
changed = False
module.exit_json(changed=changed, network=net, id=net['id'])
self.exit(changed=changed, network=net, id=net['id'])
elif state == 'absent':
if not net:
module.exit_json(changed=False)
self.exit(changed=False)
else:
cloud.delete_network(name)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
self.conn.delete_network(name)
self.exit(changed=True)
if __name__ == "__main__":
def main():
module = NetworkModule()
module()
if __name__ == '__main__':
main()

View File

@@ -113,37 +113,33 @@ openstack_networks:
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_full_argument_spec,
openstack_cloud_from_module,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
class NetworkInfoModule(OpenStackModule):
argument_spec = openstack_full_argument_spec(
deprecated_names = ('networks_facts', 'openstack.cloud.networks_facts')
argument_spec = dict(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None)
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name == 'openstack.cloud.networks_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.networks_facts' module has been renamed to 'openstack.cloud.networks_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
networks = cloud.search_networks(module.params['name'],
module.params['filters'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_networks=networks))
else:
module.exit_json(changed=False, openstack_networks=networks)
def run(self):
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
kwargs = self.check_versioned(
filters=self.params['filters']
)
if self.params['name']:
kwargs['name_or_id'] = self.params['name']
networks = self.conn.search_networks(**kwargs)
self.exit(changed=False, openstack_networks=networks)
def main():
module = NetworkInfoModule()
module()
if __name__ == '__main__':

View File

@@ -65,58 +65,55 @@ EXAMPLES = '''
container: config
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def process_object(
cloud_obj, container, name, filename, container_access, **kwargs):
changed = False
container_obj = cloud_obj.get_container(container)
if kwargs['state'] == 'present':
if not container_obj:
container_obj = cloud_obj.create_container(container)
changed = True
if cloud_obj.get_container_access(container) != container_access:
cloud_obj.set_container_access(container, container_access)
changed = True
if name:
if cloud_obj.is_object_stale(container, name, filename):
cloud_obj.create_object(container, name, filename)
changed = True
else:
if container_obj:
if name:
if cloud_obj.get_object_metadata(container, name):
cloud_obj.delete_object(container, name)
changed = True
else:
cloud_obj.delete_container(container)
changed = True
return changed
def main():
argument_spec = openstack_full_argument_spec(
class SwiftObjectModule(OpenStackModule):
argument_spec = dict(
name=dict(required=False, default=None),
container=dict(required=True),
filename=dict(required=False, default=None),
container_access=dict(default='private', choices=['private', 'public']),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
module_kwargs = dict()
sdk, cloud = openstack_cloud_from_module(module)
try:
changed = process_object(cloud, **module.params)
def process_object(
self, container, name, filename, container_access, **kwargs
):
changed = False
container_obj = self.conn.get_container(container)
if kwargs['state'] == 'present':
if not container_obj:
container_obj = self.conn.create_container(container)
changed = True
if self.conn.get_container_access(container) != container_access:
self.conn.set_container_access(container, container_access)
changed = True
if name:
if self.conn.is_object_stale(container, name, filename):
self.conn.create_object(container, name, filename)
changed = True
else:
if container_obj:
if name:
if self.conn.get_object_metadata(container, name):
self.conn.delete_object(container, name)
changed = True
else:
self.conn.delete_container(container)
changed = True
return changed
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def run(self):
changed = self.process_object(**self.params)
self.exit_json(changed=changed)
def main():
module = SwiftObjectModule()
module()
if __name__ == "__main__":

View File

@@ -0,0 +1,207 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2021 by Open Telekom Cloud, operated by T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: object_container
short_description: Manage Swift container.
author: OpenStack Ansible SIG
description:
- Manage Swift container.
options:
container:
description: Name of a container in Swift.
type: str
required: true
metadata:
description:
- Key/value pairs to be set as metadata on the container.
- If a container doesn't exist, it will be created.
- Both custom and system metadata can be set.
- Custom metadata are keys and values defined by the user.
- The system metadata keys are content_type, content_encoding, content_disposition, delete_after,\
delete_at, is_content_type_detected
type: dict
required: false
keys:
description: Keys from 'metadata' to be deleted.
type: list
elements: str
required: false
delete_with_all_objects:
description:
- Whether the container should be deleted with all objects or not.
- Without this parameter set to "true", an attempt to delete a container that contains objects will fail.
type: bool
default: False
required: false
state:
description: Whether resource should be present or absent.
default: 'present'
choices: ['present', 'absent']
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
container:
description: Specifies the container.
returned: On success when C(state=present)
type: dict
sample:
{
"bytes": 5449,
"bytes_used": 5449,
"content_type": null,
"count": 1,
"id": "otc",
"if_none_match": null,
"is_content_type_detected": null,
"is_newest": null,
"meta_temp_url_key": null,
"meta_temp_url_key_2": null,
"name": "otc",
"object_count": 1,
"read_ACL": null,
"sync_key": null,
"sync_to": null,
"timestamp": null,
"versions_location": null,
"write_ACL": null
}
'''
EXAMPLES = '''
# Create empty container
- openstack.cloud.object_container:
container: "new-container"
state: present
# Set metadata for container
- openstack.cloud.object_container:
container: "new-container"
metadata: "Cache-Control='no-cache'"
# Delete some keys from metadata of a container
- openstack.cloud.object_container:
container: "new-container"
keys:
- content_type
# Delete container
- openstack.cloud.object_container:
container: "new-container"
state: absent
# Delete container and its objects
- openstack.cloud.object_container:
container: "new-container"
delete_with_all_objects: true
state: absent
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class ContainerModule(OpenStackModule):
argument_spec = dict(
container=dict(type='str', required=True),
metadata=dict(type='dict', required=False),
keys=dict(type='list', required=False, elements='str', no_log=False),
state=dict(type='str', required=False, default='present', choices=['present', 'absent']),
delete_with_all_objects=dict(type='bool', default=False, required=False)
)
def create(self, container):
data = {}
if self._container_exist(container):
self.exit_json(changed=False)
container_data = self.conn.object_store.create_container(name=container).to_dict()
container_data.pop('location')
data['container'] = container_data
self.exit_json(changed=True, **data)
def delete(self, container):
delete_with_all_objects = self.params['delete_with_all_objects']
changed = False
if self._container_exist(container):
objects = []
for raw in self.conn.object_store.objects(container):
dt = raw.to_dict()
dt.pop('location')
objects.append(dt)
if len(objects) > 0:
if delete_with_all_objects:
for obj in objects:
self.conn.object_store.delete_object(container=container, obj=obj['id'])
else:
self.fail_json(msg="Container has objects")
self.conn.object_store.delete_container(container=container)
changed = True
self.exit(changed=changed)
def set_metadata(self, container, metadata):
data = {}
if not self._container_exist(container):
new_container = self.conn.object_store.create_container(name=container).to_dict()
new_container = self.conn.object_store.set_container_metadata(container, **metadata).to_dict()
new_container.pop('location')
data['container'] = new_container
self.exit(changed=True, **data)
def delete_metadata(self, container, keys):
if not self._container_exist(container):
self.fail_json(msg="Container doesn't exist")
self.conn.object_store.delete_container_metadata(container=container, keys=keys)
self.exit(changed=True)
def _container_exist(self, container):
try:
self.conn.object_store.get_container_metadata(container)
return True
except self.sdk.exceptions.ResourceNotFound:
return False
def run(self):
container = self.params['container']
state = self.params['state']
metadata = self.params['metadata']
keys = self.params['keys']
if state == 'absent':
self.delete(container)
if metadata:
self.set_metadata(container, metadata)
if keys:
self.delete_metadata(container, keys)
self.create(container)
def main():
module = ContainerModule()
module()
if __name__ == "__main__":
main()

View File

@@ -119,6 +119,10 @@ options:
description:
- Whether to enable or disable the port security on the network.
type: bool
binding_profile:
description:
- Binding profile dict that the port should be created with.
type: dict
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -199,6 +203,20 @@ EXAMPLES = '''
name: port1
network: foo
vnic_type: direct
# Create a port with binding profile
- openstack.cloud.port:
state: present
auth:
auth_url: https://identity.example.com
username: admin
password: admin
project_name: admin
name: port1
network: foo
binding_profile:
"pci_slot": "0000:03:11.1"
"physical_network": "provider"
'''
RETURN = '''
@@ -246,12 +264,14 @@ port_security_enabled:
description: Port security state on the network.
returned: success
type: bool
binding:profile:
description: Port binded profile
returned: success
type: dict
'''
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible.module_utils.basic import missing_required_lib
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
try:
from collections import OrderedDict
@@ -264,120 +284,8 @@ except ImportError:
HAS_ORDEREDDICT = False
def _needs_update(module, port, cloud):
"""Check for differences in the updatable values.
NOTE: We don't currently allow name updates.
"""
compare_simple = ['admin_state_up',
'mac_address',
'device_owner',
'device_id',
'binding:vnic_type',
'port_security_enabled']
compare_list_dict = ['allowed_address_pairs',
'extra_dhcp_opts']
compare_list = ['security_groups']
for key in compare_simple:
if module.params[key] is not None and module.params[key] != port[key]:
return True
for key in compare_list:
if (
module.params[key] is not None
and set(module.params[key]) != set(port[key])
):
return True
for key in compare_list_dict:
if not module.params[key]:
if not port[key]:
return True
# sort dicts in list
port_ordered = [OrderedDict(sorted(d.items())) for d in port[key]]
param_ordered = [OrderedDict(sorted(d.items())) for d in module.params[key]]
for d in param_ordered:
if d not in port_ordered:
return True
for d in port_ordered:
if d not in param_ordered:
return True
# NOTE: if port was created or updated with 'no_security_groups=True',
# subsequent updates without 'no_security_groups' flag or
# 'no_security_groups=False' and no specified 'security_groups', will not
# result in an update to the port where the default security group is
# applied.
if module.params['no_security_groups'] and port['security_groups'] != []:
return True
if module.params['fixed_ips'] is not None:
for item in module.params['fixed_ips']:
if 'ip_address' in item:
# if ip_address in request does not match any in existing port,
# update is required.
if not any(match['ip_address'] == item['ip_address']
for match in port['fixed_ips']):
return True
if 'subnet_id' in item:
return True
for item in port['fixed_ips']:
# if ip_address in existing port does not match any in request,
# update is required.
if not any(match.get('ip_address') == item['ip_address']
for match in module.params['fixed_ips']):
return True
return False
def _system_state_change(module, port, cloud):
state = module.params['state']
if state == 'present':
if not port:
return True
return _needs_update(module, port, cloud)
if state == 'absent' and port:
return True
return False
def _compose_port_args(module, cloud):
port_kwargs = {}
optional_parameters = ['name',
'fixed_ips',
'admin_state_up',
'mac_address',
'security_groups',
'allowed_address_pairs',
'extra_dhcp_opts',
'device_owner',
'device_id',
'binding:vnic_type',
'port_security_enabled']
for optional_param in optional_parameters:
if module.params[optional_param] is not None:
port_kwargs[optional_param] = module.params[optional_param]
if module.params['no_security_groups']:
port_kwargs['security_groups'] = []
return port_kwargs
def get_security_group_id(module, cloud, security_group_name_or_id):
security_group = cloud.get_security_group(security_group_name_or_id)
if not security_group:
module.fail_json(msg="Security group: %s, was not found"
% security_group_name_or_id)
return security_group['id']
def main():
argument_spec = openstack_full_argument_spec(
class NetworkPortModule(OpenStackModule):
argument_spec = dict(
network=dict(required=False),
name=dict(required=False),
fixed_ips=dict(type='list', default=None, elements='dict'),
@@ -393,81 +301,197 @@ def main():
vnic_type=dict(default=None,
choices=['normal', 'direct', 'direct-physical',
'macvtap', 'baremetal', 'virtio-forwarder']),
port_security_enabled=dict(default=None, type='bool')
port_security_enabled=dict(default=None, type='bool'),
binding_profile=dict(default=None, type='dict')
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[
['no_security_groups', 'security_groups'],
]
],
supports_check_mode=True
)
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _needs_update(self, port):
"""Check for differences in the updatable values.
if not HAS_ORDEREDDICT:
module.fail_json(msg=missing_required_lib('ordereddict'))
NOTE: We don't currently allow name updates.
"""
compare_simple = ['admin_state_up',
'mac_address',
'device_owner',
'device_id',
'binding:vnic_type',
'port_security_enabled',
'binding:profile']
compare_list_dict = ['allowed_address_pairs',
'extra_dhcp_opts']
compare_list = ['security_groups']
name = module.params['name']
state = module.params['state']
for key in compare_simple:
if self.params[key] is not None and self.params[key] != port[key]:
return True
for key in compare_list:
if (
self.params[key] is not None
and set(self.params[key]) != set(port[key])
):
return True
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['security_groups']:
for key in compare_list_dict:
if not self.params[key]:
if port[key]:
return True
if self.params[key]:
if not port[key]:
return True
# sort dicts in list
port_ordered = [OrderedDict(sorted(d.items())) for d in port[key]]
param_ordered = [OrderedDict(sorted(d.items())) for d in self.params[key]]
for d in param_ordered:
if d not in port_ordered:
return True
for d in port_ordered:
if d not in param_ordered:
return True
# NOTE: if port was created or updated with 'no_security_groups=True',
# subsequent updates without 'no_security_groups' flag or
# 'no_security_groups=False' and no specified 'security_groups', will not
# result in an update to the port where the default security group is
# applied.
if self.params['no_security_groups'] and port['security_groups'] != []:
return True
if self.params['fixed_ips'] is not None:
for item in self.params['fixed_ips']:
if 'ip_address' in item:
# if ip_address in request does not match any in existing port,
# update is required.
if not any(match['ip_address'] == item['ip_address']
for match in port['fixed_ips']):
return True
if 'subnet_id' in item:
return True
for item in port['fixed_ips']:
# if ip_address in existing port does not match any in request,
# update is required.
if not any(match.get('ip_address') == item['ip_address']
for match in self.params['fixed_ips']):
return True
return False
def _system_state_change(self, port):
state = self.params['state']
if state == 'present':
if not port:
return True
return self._needs_update(port)
if state == 'absent' and port:
return True
return False
def _compose_port_args(self):
port_kwargs = {}
optional_parameters = ['name',
'fixed_ips',
'admin_state_up',
'mac_address',
'security_groups',
'allowed_address_pairs',
'extra_dhcp_opts',
'device_owner',
'device_id',
'binding:vnic_type',
'port_security_enabled',
'binding:profile']
for optional_param in optional_parameters:
if self.params[optional_param] is not None:
port_kwargs[optional_param] = self.params[optional_param]
if self.params['no_security_groups']:
port_kwargs['security_groups'] = []
return port_kwargs
def get_security_group_id(self, security_group_name_or_id):
security_group = self.conn.get_security_group(security_group_name_or_id)
if not security_group:
self.fail_json(msg="Security group: %s, was not found"
% security_group_name_or_id)
return security_group['id']
def run(self):
if not HAS_ORDEREDDICT:
self.fail_json(msg=missing_required_lib('ordereddict'))
name = self.params['name']
state = self.params['state']
if self.params['security_groups']:
# translate security_groups to UUID's if names where provided
module.params['security_groups'] = [
get_security_group_id(module, cloud, v)
for v in module.params['security_groups']
self.params['security_groups'] = [
self.get_security_group_id(v)
for v in self.params['security_groups']
]
# Neutron API accept 'binding:vnic_type' as an argument
# for the port type.
module.params['binding:vnic_type'] = module.params.pop('vnic_type')
self.params['binding:vnic_type'] = self.params.pop('vnic_type')
# Neutron API accept 'binding:profile' as an argument
# for the port binding profile type.
self.params['binding:profile'] = self.params.pop('binding_profile')
port = None
network_id = None
if name:
port = cloud.get_port(name)
port = self.conn.get_port(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, port, cloud))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(port))
changed = False
if state == 'present':
if not port:
network = module.params['network']
network = self.params['network']
if not network:
module.fail_json(
self.fail_json(
msg="Parameter 'network' is required in Port Create"
)
port_kwargs = _compose_port_args(module, cloud)
network_object = cloud.get_network(network)
port_kwargs = self._compose_port_args()
network_object = self.conn.get_network(network)
if network_object:
network_id = network_object['id']
else:
module.fail_json(
self.fail_json(
msg="Specified network was not found."
)
port = cloud.create_port(network_id, **port_kwargs)
port = self.conn.create_port(network_id, **port_kwargs)
changed = True
else:
if _needs_update(module, port, cloud):
port_kwargs = _compose_port_args(module, cloud)
port = cloud.update_port(port['id'], **port_kwargs)
if self._needs_update(port):
port_kwargs = self._compose_port_args()
port = self.conn.update_port(port['id'], **port_kwargs)
changed = True
module.exit_json(changed=changed, id=port['id'], port=port)
self.exit_json(changed=changed, id=port['id'], port=port)
if state == 'absent':
if port:
cloud.delete_port(port['id'])
self.conn.delete_port(port['id'])
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = NetworkPortModule()
module()
if __name__ == '__main__':

View File

@@ -179,38 +179,30 @@ openstack_ports:
sample: "51fce036d7984ba6af4f6c849f65ef00"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class NetworkPortInfoModule(OpenStackModule):
argument_spec = dict(
port=dict(required=False),
filters=dict(type='dict', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.port_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.port_facts' module has been renamed to 'openstack.cloud.port_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
module_kwargs = dict(
)
port = module.params.get('port')
filters = module.params.get('filters')
deprecated_names = ('openstack.cloud.port_facts')
sdk, cloud = openstack_cloud_from_module(module)
try:
ports = cloud.search_ports(port, filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_ports=ports))
else:
module.exit_json(changed=False, openstack_ports=ports)
def run(self):
port = self.params.get('port')
filters = self.params.get('filters')
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
ports = self.conn.search_ports(port, filters)
self.exit_json(changed=False, openstack_ports=ports)
def main():
module = NetworkPortInfoModule()
module()
if __name__ == '__main__':

View File

@@ -38,7 +38,7 @@ options:
- Additional properties to be associated with this project. Requires
openstacksdk>0.45.
type: dict
default: {}
required: false
state:
description:
- Should the resource be present or absent.
@@ -99,133 +99,121 @@ project:
sample: True
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, project):
keys = ('description', 'enabled')
for key in keys:
if module.params[key] is not None and module.params[key] != project.get(key):
return True
properties = module.params['properties']
if properties:
project_properties = project.get('properties')
for k, v in properties.items():
if v is not None and v != project_properties[k]:
return True
return False
def _system_state_change(module, project):
state = module.params['state']
if state == 'present':
if project is None:
changed = True
else:
if _needs_update(module, project):
changed = True
else:
changed = False
elif state == 'absent':
if project is None:
changed = False
else:
changed = True
return changed
def main():
argument_spec = openstack_full_argument_spec(
class IdentityProjectModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
description=dict(required=False, default=None),
domain_id=dict(required=False, default=None, aliases=['domain']),
properties=dict(type='dict', default={}),
description=dict(required=False),
domain_id=dict(required=False, aliases=['domain']),
properties=dict(required=False, type='dict', min_ver='0.45.1'),
enabled=dict(default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present'])
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
supports_check_mode=True,
**module_kwargs
module_kwargs = dict(
supports_check_mode=True
)
name = module.params['name']
description = module.params['description']
domain = module.params.get('domain_id')
enabled = module.params['enabled']
properties = module.params['properties']
state = module.params['state']
def _needs_update(self, project):
keys = ('description', 'enabled')
for key in keys:
if self.params[key] is not None and self.params[key] != project.get(key):
return True
min_version = None
properties = self.params['properties']
if properties:
project_properties = project.get('properties')
for k, v in properties.items():
if v is not None and (k not in project_properties or v != project_properties[k]):
return True
if properties:
min_version = '0.45.1'
return False
def _system_state_change(self, project):
state = self.params['state']
if state == 'present':
if project is None:
changed = True
else:
if self._needs_update(project):
changed = True
else:
changed = False
elif state == 'absent':
changed = project is not None
return changed
def run(self):
name = self.params['name']
description = self.params['description']
domain = self.params['domain_id']
enabled = self.params['enabled']
properties = self.params['properties'] or {}
state = self.params['state']
sdk, cloud = openstack_cloud_from_module(module, min_version)
try:
if domain:
try:
# We assume admin is passing domain id
dom = cloud.get_domain(domain)['id']
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
try:
dom = cloud.search_domains(filters={'name': domain})[0]['id']
dom = self.conn.search_domains(filters={'name': domain})[0]['id']
domain = dom
except Exception:
# Ok, let's hope the user is non-admin and passing a sane id
pass
if domain:
project = cloud.get_project(name, domain_id=domain)
project = self.conn.get_project(name, domain_id=domain)
else:
project = cloud.get_project(name)
project = self.conn.get_project(name)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, project))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(project))
if state == 'present':
if project is None:
project = cloud.create_project(
project = self.conn.create_project(
name=name, description=description,
domain_id=domain,
enabled=enabled)
changed = True
project = cloud.update_project(
project['id'], description=description,
enabled=enabled, **properties)
project = self.conn.update_project(
project['id'],
description=description,
enabled=enabled,
**properties)
else:
if _needs_update(module, project):
project = cloud.update_project(
project['id'], description=description,
enabled=enabled, **properties)
if self._needs_update(project):
project = self.conn.update_project(
project['id'],
description=description,
enabled=enabled,
**properties)
changed = True
else:
changed = False
module.exit_json(changed=changed, project=project)
self.exit_json(changed=changed, project=project)
elif state == 'absent':
if project is None:
changed = False
else:
cloud.delete_project(project['id'])
self.conn.delete_project(project['id'])
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=e.message, extra_data=e.extra_data)
def main():
module = IdentityProjectModule()
module()
if __name__ == '__main__':

View File

@@ -91,49 +91,41 @@ flavor:
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
argument_spec = openstack_full_argument_spec(
class IdentityProjectAccess(OpenStackModule):
argument_spec = dict(
state=dict(required=False, default='present',
choices=['absent', 'present']),
target_project_id=dict(required=True, type='str'),
resource_type=dict(required=True, type='str'),
resource_name=dict(required=True, type='str'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
module_kwargs = dict(
supports_check_mode=True,
required_if=[
('state', 'present', ['target_project_id'])
],
**module_kwargs)
]
)
sdk, cloud = openstack_cloud_from_module(module)
def run(self):
state = self.params['state']
resource_name = self.params['resource_name']
resource_type = self.params['resource_type']
target_project_id = self.params['target_project_id']
state = module.params['state']
resource_name = module.params['resource_name']
resource_type = module.params['resource_type']
target_project_id = module.params['target_project_id']
try:
if resource_type == 'nova_flavor':
# returns Munch({'NAME_ATTR': 'name',
# 'tenant_id': u'37e55da59ec842649d84230f3a24eed5',
# 'HUMAN_ID': False,
# 'flavor_id': u'6d4d37b9-0480-4a8c-b8c9-f77deaad73f9',
# 'request_ids': [], 'human_id': None}),
_get_resource = cloud.get_flavor
_list_resource_access = cloud.list_flavor_access
_add_resource_access = cloud.add_flavor_access
_remove_resource_access = cloud.remove_flavor_access
_get_resource = self.conn.get_flavor
_list_resource_access = self.conn.list_flavor_access
_add_resource_access = self.conn.add_flavor_access
_remove_resource_access = self.conn.remove_flavor_access
elif resource_type == 'cinder_volume_type':
# returns [Munch({
# 'project_id': u'178cdb9955b047eea7afbe582038dc94',
@@ -141,41 +133,43 @@ def main():
# 'human_id': None,
# 'HUMAN_ID': False},
# 'id': u'd5573023-b290-42c8-b232-7c5ca493667f'}),
_get_resource = cloud.get_volume_type
_list_resource_access = cloud.get_volume_type_access
_add_resource_access = cloud.add_volume_type_access
_remove_resource_access = cloud.remove_volume_type_access
_get_resource = self.conn.get_volume_type
_list_resource_access = self.conn.get_volume_type_access
_add_resource_access = self.conn.add_volume_type_access
_remove_resource_access = self.conn.remove_volume_type_access
else:
module.exit_json(changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Not implemented.")
self.exit_json(
changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Not implemented.")
resource = _get_resource(resource_name)
if not resource:
module.exit_json(changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Not found.")
self.exit_json(
changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Not found.")
resource_id = getattr(resource, 'id', resource['id'])
# _list_resource_access returns a list of dicts containing 'project_id'
acls = _list_resource_access(resource_id)
if not all(acl.get('project_id') for acl in acls):
module.exit_json(changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Missing project_id in resource output.")
self.exit_json(
changed=False,
resource_name=resource_name,
resource_type=resource_type,
error="Missing project_id in resource output.")
allowed_tenants = [acl['project_id'] for acl in acls]
changed_access = any((
state == 'present' and target_project_id not in allowed_tenants,
state == 'absent' and target_project_id in allowed_tenants
))
if module.check_mode or not changed_access:
module.exit_json(changed=changed_access,
resource=resource,
id=resource_id)
if self.ansible.check_mode or not changed_access:
self.exit_json(
changed=changed_access, resource=resource, id=resource_id)
if state == 'present':
_add_resource_access(
@@ -186,12 +180,13 @@ def main():
resource_id, target_project_id
)
module.exit_json(changed=True,
resource=resource,
id=resource_id)
self.exit_json(
changed=True, resource=resource, id=resource_id)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), **module.params)
def main():
module = IdentityProjectAccess()
module()
if __name__ == '__main__':

View File

@@ -99,58 +99,57 @@ openstack_projects:
type: bool
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
class IdentityProjectInfoModule(OpenStackModule):
deprecated_names = ('project_facts', 'openstack.cloud.project_facts')
argument_spec = openstack_full_argument_spec(
name=dict(required=False, default=None),
domain=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None),
argument_spec = dict(
name=dict(required=False),
domain=dict(required=False),
filters=dict(required=False, type='dict'),
)
module_kwargs = dict(
supports_check_mode=True
)
module = AnsibleModule(argument_spec)
is_old_facts = module._name == 'openstack.cloud.project_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.project_facts' module has been renamed to 'openstack.cloud.project_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, opcloud = openstack_cloud_from_module(module)
try:
name = module.params['name']
domain = module.params['domain']
filters = module.params['filters']
def run(self):
name = self.params['name']
domain = self.params['domain']
filters = self.params['filters']
is_old_facts = self.module_name == 'openstack.cloud.project_facts'
if domain:
try:
# We assume admin is passing domain id
dom = opcloud.get_domain(domain)['id']
dom = self.conn.get_domain(domain)['id']
domain = dom
except Exception:
# If we fail, maybe admin is passing a domain name.
# Note that domains have unique names, just like id.
dom = opcloud.search_domains(filters={'name': domain})
dom = self.conn.search_domains(filters={'name': domain})
if dom:
domain = dom[0]['id']
else:
module.fail_json(msg='Domain name or ID does not exist')
self.fail_json(msg='Domain name or ID does not exist')
if not filters:
filters = {}
filters['domain_id'] = domain
projects = opcloud.search_projects(name, filters)
projects = self.conn.search_projects(name, filters)
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
self.exit_json(changed=False, ansible_facts=dict(
openstack_projects=projects))
else:
module.exit_json(changed=False, openstack_projects=projects)
self.exit_json(changed=False, openstack_projects=projects)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityProjectInfoModule()
module()
if __name__ == '__main__':

View File

@@ -256,117 +256,11 @@ openstack_quotas:
'''
import traceback
KEYSTONEAUTH1_IMP_ERR = None
try:
from keystoneauth1 import exceptions as ksa_exceptions
HAS_KEYSTONEAUTH1 = True
except ImportError:
KEYSTONEAUTH1_IMP_ERR = traceback.format_exc()
HAS_KEYSTONEAUTH1 = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_full_argument_spec,
openstack_cloud_from_module,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _get_volume_quotas(cloud, project):
return cloud.get_volume_quotas(project)
def _get_network_quotas(cloud, project):
return cloud.get_network_quotas(project)
def _get_compute_quotas(cloud, project):
return cloud.get_compute_quotas(project)
def _get_quotas(sdk, module, cloud, project):
quota = {}
try:
quota['volume'] = _get_volume_quotas(cloud, project)
except ksa_exceptions.EndpointNotFound:
module.warn("No public endpoint for volumev2 service was found. Ignoring volume quotas.")
try:
quota['network'] = _get_network_quotas(cloud, project)
except ksa_exceptions.EndpointNotFound:
module.warn("No public endpoint for network service was found. Ignoring network quotas.")
quota['compute'] = _get_compute_quotas(cloud, project)
for quota_type in quota.keys():
quota[quota_type] = _scrub_results(quota[quota_type])
return quota
def _scrub_results(quota):
filter_attr = [
'HUMAN_ID',
'NAME_ATTR',
'human_id',
'request_ids',
'x_openstack_request_ids',
]
for attr in filter_attr:
if attr in quota:
del quota[attr]
return quota
def _system_state_change_details(module, project_quota_output):
quota_change_request = {}
changes_required = False
for quota_type in project_quota_output.keys():
for quota_option in project_quota_output[quota_type].keys():
if quota_option in module.params and module.params[quota_option] is not None:
if project_quota_output[quota_type][quota_option] != module.params[quota_option]:
changes_required = True
if quota_type not in quota_change_request:
quota_change_request[quota_type] = {}
quota_change_request[quota_type][quota_option] = module.params[quota_option]
return (changes_required, quota_change_request)
def _system_state_change(module, project_quota_output):
"""
Determine if changes are required to the current project quota.
This is done by comparing the current project_quota_output against
the desired quota settings set on the module params.
"""
changes_required, quota_change_request = _system_state_change_details(
module,
project_quota_output
)
if changes_required:
return True
else:
return False
def main():
argument_spec = openstack_full_argument_spec(
class QuotaModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']),
backup_gigabytes=dict(required=False, type='int', default=None),
@@ -381,7 +275,7 @@ def main():
injected_files=dict(required=False, type='int', default=None),
injected_path_size=dict(required=False, type='int', default=None),
instances=dict(required=False, type='int', default=None),
key_pairs=dict(required=False, type='int', default=None),
key_pairs=dict(required=False, type='int', default=None, no_log=False),
loadbalancer=dict(required=False, type='int', default=None),
network=dict(required=False, type='int', default=None),
per_volume_gigabytes=dict(required=False, type='int', default=None),
@@ -404,16 +298,89 @@ def main():
volumes_types=dict(required=False, type='dict', default={})
)
module = AnsibleModule(argument_spec,
supports_check_mode=True
)
module_kwargs = dict(
supports_check_mode=True
)
if not HAS_KEYSTONEAUTH1:
module.fail_json(msg=missing_required_lib("keystoneauth1"), exception=KEYSTONEAUTH1_IMP_ERR)
def _get_volume_quotas(self, project):
return self.conn.get_volume_quotas(project)
sdk, cloud = openstack_cloud_from_module(module)
try:
cloud_params = dict(module.params)
def _get_network_quotas(self, project):
return self.conn.get_network_quotas(project)
def _get_compute_quotas(self, project):
return self.conn.get_compute_quotas(project)
def _get_quotas(self, project):
quota = {}
try:
quota['volume'] = self._get_volume_quotas(project)
except Exception:
self.warn("No public endpoint for volumev2 service was found. Ignoring volume quotas.")
try:
quota['network'] = self._get_network_quotas(project)
except Exception:
self.warn("No public endpoint for network service was found. Ignoring network quotas.")
quota['compute'] = self._get_compute_quotas(project)
for quota_type in quota.keys():
quota[quota_type] = self._scrub_results(quota[quota_type])
return quota
def _scrub_results(self, quota):
filter_attr = [
'HUMAN_ID',
'NAME_ATTR',
'human_id',
'request_ids',
'x_openstack_request_ids',
]
for attr in filter_attr:
if attr in quota:
del quota[attr]
return quota
def _system_state_change_details(self, project_quota_output):
quota_change_request = {}
changes_required = False
for quota_type in project_quota_output.keys():
for quota_option in project_quota_output[quota_type].keys():
if quota_option in self.params and self.params[quota_option] is not None:
if project_quota_output[quota_type][quota_option] != self.params[quota_option]:
changes_required = True
if quota_type not in quota_change_request:
quota_change_request[quota_type] = {}
quota_change_request[quota_type][quota_option] = self.params[quota_option]
return (changes_required, quota_change_request)
def _system_state_change(self, project_quota_output):
"""
Determine if changes are required to the current project quota.
This is done by comparing the current project_quota_output against
the desired quota settings set on the module params.
"""
changes_required, quota_change_request = self._system_state_change_details(
project_quota_output
)
if changes_required:
return True
else:
return False
def run(self):
cloud_params = dict(self.params)
# In order to handle the different volume types we update module params after.
dynamic_types = [
@@ -423,20 +390,19 @@ def main():
]
for dynamic_type in dynamic_types:
for k, v in module.params[dynamic_type].items():
module.params[k] = int(v)
for k, v in self.params[dynamic_type].items():
self.params[k] = int(v)
# Get current quota values
project_quota_output = _get_quotas(
sdk, module, cloud, cloud_params['name'])
project_quota_output = self._get_quotas(cloud_params['name'])
changes_required = False
if module.params['state'] == "absent":
if self.params['state'] == "absent":
# If a quota state is set to absent we should assume there will be changes.
# The default quota values are not accessible so we can not determine if
# no changes will occur or not.
if module.check_mode:
module.exit_json(changed=True)
if self.ansible.check_mode:
self.exit_json(changed=True)
# Calling delete_network_quotas when a quota has not been set results
# in an error, according to the sdk docs it should return the
@@ -447,49 +413,48 @@ def main():
neutron_msg2 = "could not be found"
for quota_type in project_quota_output.keys():
quota_call = getattr(cloud, 'delete_%s_quotas' % (quota_type))
quota_call = getattr(self.conn, 'delete_%s_quotas' % (quota_type))
try:
quota_call(cloud_params['name'])
except sdk.exceptions.OpenStackCloudException as e:
except Exception as e:
error_msg = str(e)
if error_msg.find(neutron_msg1) > -1 and error_msg.find(neutron_msg2) > -1:
pass
else:
module.fail_json(msg=str(e), extra_data=e.extra_data)
self.fail_json(msg=str(e), extra_data=e.extra_data)
project_quota_output = _get_quotas(
sdk, module, cloud, cloud_params['name'])
project_quota_output = self._get_quotas(cloud_params['name'])
changes_required = True
elif module.params['state'] == "present":
if module.check_mode:
module.exit_json(changed=_system_state_change(module, project_quota_output))
elif self.params['state'] == "present":
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(
project_quota_output))
changes_required, quota_change_request = _system_state_change_details(
module,
changes_required, quota_change_request = self._system_state_change_details(
project_quota_output
)
if changes_required:
for quota_type in quota_change_request.keys():
quota_call = getattr(cloud, 'set_%s_quotas' % (quota_type))
quota_call = getattr(self.conn, 'set_%s_quotas' % (quota_type))
quota_call(cloud_params['name'], **quota_change_request[quota_type])
# Get quota state post changes for validation
project_quota_update = _get_quotas(
sdk, module, cloud, cloud_params['name'])
project_quota_update = self._get_quotas(cloud_params['name'])
if project_quota_output == project_quota_update:
module.fail_json(msg='Could not apply quota update')
self.fail_json(msg='Could not apply quota update')
project_quota_output = project_quota_update
module.exit_json(changed=changes_required,
openstack_quotas=project_quota_output
)
self.exit_json(
changed=changes_required, openstack_quotas=project_quota_output)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
def main():
module = QuotaModule()
module()
if __name__ == '__main__':

View File

@@ -19,13 +19,14 @@ options:
type: str
name:
description:
- Name of the recordset
- Name of the recordset. It must be ended with name of dns zone.
required: true
type: str
recordset_type:
description:
- Recordset type
- Required when I(state=present).
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
type: str
records:
description:
@@ -61,8 +62,8 @@ EXAMPLES = '''
cloud: mycloud
state: present
zone: example.net.
name: www
recordset_type: primary
name: www.example.net.
recordset_type: "a"
records: ['10.1.1.1']
description: test recordset
ttl: 3600
@@ -72,7 +73,9 @@ EXAMPLES = '''
cloud: mycloud
state: present
zone: example.net.
name: www
name: www.example.net.
recordset_type: "a"
records: ['10.1.1.1']
ttl: 7200
# Delete recordset named "www.example.net."
@@ -80,7 +83,7 @@ EXAMPLES = '''
cloud: mycloud
state: absent
zone: example.net.
name: www
name: www.example.net.
'''
RETURN = '''
@@ -119,116 +122,116 @@ recordset:
sample: ['10.0.0.1']
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, records, description, ttl, zone, recordset):
if state == 'present':
if recordset is None:
return True
if records is not None and recordset['records'] != records:
return True
if description is not None and recordset['description'] != description:
return True
if ttl is not None and recordset['ttl'] != ttl:
return True
if state == 'absent' and recordset:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class DnsRecordsetModule(OpenStackModule):
argument_spec = dict(
zone=dict(required=True),
name=dict(required=True),
recordset_type=dict(required=False),
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
records=dict(required=False, type='list', elements='str'),
description=dict(required=False, default=None),
ttl=dict(required=False, default=None, type='int'),
ttl=dict(required=False, type='int'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
required_if=[
('state', 'present',
['recordset_type', 'records'])],
supports_check_mode=True,
**module_kwargs)
module_kwargs = dict(
required_if=[
('state', 'present',
['recordset_type', 'records'])],
supports_check_mode=True
)
zone = module.params.get('zone')
name = module.params.get('name')
state = module.params.get('state')
module_min_sdk_version = '0.28.0'
sdk, cloud = openstack_cloud_from_module(module)
try:
recordset_type = module.params.get('recordset_type')
recordset_filter = {'type': recordset_type}
def _system_state_change(self, state, records, description, ttl, recordset):
if state == 'present':
if recordset is None:
return True
if records is not None and recordset['records'] != records:
return True
if description is not None and recordset['description'] != description:
return True
if ttl is not None and recordset['ttl'] != ttl:
return True
if state == 'absent' and recordset:
return True
return False
recordsets = cloud.search_recordsets(zone, name_or_id=name, filters=recordset_filter)
def run(self):
zone = self.params.get('zone')
name = self.params.get('name')
state = self.params.get('state')
if len(recordsets) == 1:
recordsets = self.conn.search_recordsets(zone, name_or_id=name)
if recordsets:
recordset = recordsets[0]
try:
recordset_id = recordset['id']
except KeyError as e:
module.fail_json(msg=str(e))
self.fail_json(msg=str(e))
else:
# recordsets is filtered by type and should never be more than 1 return
recordset = None
if state == 'present':
records = module.params.get('records')
description = module.params.get('description')
ttl = module.params.get('ttl')
recordset_type = self.params.get('recordset_type').upper()
records = self.params.get('records')
description = self.params.get('description')
ttl = self.params.get('ttl')
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
records, description,
ttl, zone,
recordset))
kwargs = {}
if description:
kwargs['description'] = description
kwargs['records'] = records
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(
state, records, description, ttl, recordset))
if recordset is None:
recordset = cloud.create_recordset(
if ttl:
kwargs['ttl'] = ttl
else:
kwargs['ttl'] = 300
recordset = self.conn.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
records=records, description=description, ttl=ttl)
**kwargs)
changed = True
else:
if records is None:
records = []
if ttl:
kwargs['ttl'] = ttl
pre_update_recordset = recordset
changed = _system_state_change(state, records,
description, ttl,
zone, pre_update_recordset)
changed = self._system_state_change(
state, records, description, ttl, pre_update_recordset)
if changed:
zone = cloud.update_recordset(
zone, recordset_id,
records=records,
description=description,
ttl=ttl)
recordset = self.conn.update_recordset(
zone=zone, name_or_id=recordset_id, **kwargs)
module.exit_json(changed=changed, recordset=recordset)
self.exit_json(changed=changed, recordset=recordset)
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
None, None,
None,
None, recordset))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(
state, None, None, None, recordset))
if recordset is None:
changed = False
else:
cloud.delete_recordset(zone, recordset_id)
self.conn.delete_recordset(zone, recordset_id)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = DnsRecordsetModule()
module()
if __name__ == '__main__':

View File

@@ -72,35 +72,11 @@ RETURN = '''
#
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _system_state_change(state, assignment):
if state == 'present' and not assignment:
return True
elif state == 'absent' and assignment:
return True
return False
def _build_kwargs(user, group, project, domain):
kwargs = {}
if user:
kwargs['user'] = user
if group:
kwargs['group'] = group
if project:
kwargs['project'] = project
if domain:
kwargs['domain'] = domain
return kwargs
def main():
argument_spec = openstack_full_argument_spec(
class IdentityRoleAssignmentModule(OpenStackModule):
argument_spec = dict(
role=dict(required=True),
user=dict(required=False),
group=dict(required=False),
@@ -109,92 +85,111 @@ def main():
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
required_one_of=[
['user', 'group']
])
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
],
supports_check_mode=True
)
role = module.params.get('role')
user = module.params.get('user')
group = module.params.get('group')
project = module.params.get('project')
domain = module.params.get('domain')
state = module.params.get('state')
def _system_state_change(self, state, assignment):
if state == 'present' and not assignment:
return True
elif state == 'absent' and assignment:
return True
return False
def _build_kwargs(self, user, group, project, domain):
kwargs = {}
if user:
kwargs['user'] = user
if group:
kwargs['group'] = group
if project:
kwargs['project'] = project
if domain:
kwargs['domain'] = domain
return kwargs
def run(self):
role = self.params.get('role')
user = self.params.get('user')
group = self.params.get('group')
project = self.params.get('project')
domain = self.params.get('domain')
state = self.params.get('state')
sdk, cloud = openstack_cloud_from_module(module)
try:
filters = {}
domain_id = None
r = cloud.get_role(role)
r = self.conn.get_role(role)
if r is None:
module.fail_json(msg="Role %s is not valid" % role)
self.fail_json(msg="Role %s is not valid" % role)
filters['role'] = r['id']
if domain:
d = cloud.get_domain(name_or_id=domain)
d = self.conn.get_domain(name_or_id=domain)
if d is None:
module.fail_json(msg="Domain %s is not valid" % domain)
self.fail_json(msg="Domain %s is not valid" % domain)
filters['domain'] = d['id']
domain_id = d['id']
if user:
if domain:
u = cloud.get_user(user, domain_id=filters['domain'])
u = self.conn.get_user(user, domain_id=filters['domain'])
else:
u = cloud.get_user(user)
u = self.conn.get_user(user)
if u is None:
module.fail_json(msg="User %s is not valid" % user)
self.fail_json(msg="User %s is not valid" % user)
filters['user'] = u['id']
if group:
if domain:
g = cloud.get_group(group, domain_id=filters['domain'])
g = self.conn.get_group(group, domain_id=filters['domain'])
else:
g = cloud.get_group(group)
g = self.conn.get_group(group)
if g is None:
module.fail_json(msg="Group %s is not valid" % group)
self.fail_json(msg="Group %s is not valid" % group)
filters['group'] = g['id']
if project:
if domain:
p = cloud.get_project(project, domain_id=filters['domain'])
p = self.conn.get_project(project, domain_id=filters['domain'])
# OpenStack won't allow us to use both a domain and project as
# filter. Once we identified the project (using the domain as
# a filter criteria), we need to remove the domain itself from
# the filters list.
domain_id = filters.pop('domain')
else:
p = cloud.get_project(project)
p = self.conn.get_project(project)
if p is None:
module.fail_json(msg="Project %s is not valid" % project)
self.fail_json(msg="Project %s is not valid" % project)
filters['project'] = p['id']
assignment = cloud.list_role_assignments(filters=filters)
assignment = self.conn.list_role_assignments(filters=filters)
if module.check_mode:
module.exit_json(changed=_system_state_change(state, assignment))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(state, assignment))
changed = False
if state == 'present':
if not assignment:
kwargs = _build_kwargs(user, group, project, domain_id)
cloud.grant_role(role, **kwargs)
kwargs = self._build_kwargs(user, group, project, domain_id)
self.conn.grant_role(role, **kwargs)
changed = True
elif state == 'absent':
if assignment:
kwargs = _build_kwargs(user, group, project, domain_id)
cloud.revoke_role(role, **kwargs)
kwargs = self._build_kwargs(user, group, project, domain_id)
self.conn.revoke_role(role, **kwargs)
changed = True
module.exit_json(changed=changed)
self.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = IdentityRoleAssignmentModule()
module()
if __name__ == '__main__':

View File

@@ -210,10 +210,8 @@ router:
type: list
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
ROUTER_INTERFACE_OWNERS = set([
'network:router_interface',
@@ -222,160 +220,8 @@ ROUTER_INTERFACE_OWNERS = set([
])
def _router_internal_interfaces(cloud, router):
for port in cloud.list_router_interfaces(router, 'internal'):
if port['device_owner'] in ROUTER_INTERFACE_OWNERS:
yield port
def _needs_update(cloud, module, router, network, internal_subnet_ids, internal_port_ids, filters=None):
"""Decide if the given router needs an update.
"""
if router['admin_state_up'] != module.params['admin_state_up']:
return True
if router['external_gateway_info']:
# check if enable_snat is set in module params
if module.params['enable_snat'] is not None:
if router['external_gateway_info'].get('enable_snat', True) != module.params['enable_snat']:
return True
if network:
if not router['external_gateway_info']:
return True
elif router['external_gateway_info']['network_id'] != network['id']:
return True
# check external interfaces
if module.params['external_fixed_ips']:
for new_iface in module.params['external_fixed_ips']:
subnet = cloud.get_subnet(new_iface['subnet'], filters)
exists = False
# compare the requested interface with existing, looking for an existing match
for existing_iface in router['external_gateway_info']['external_fixed_ips']:
if existing_iface['subnet_id'] == subnet['id']:
if 'ip' in new_iface:
if existing_iface['ip_address'] == new_iface['ip']:
# both subnet id and ip address match
exists = True
break
else:
# only the subnet was given, so ip doesn't matter
exists = True
break
# this interface isn't present on the existing router
if not exists:
return True
# check internal interfaces
if module.params['interfaces']:
existing_subnet_ids = []
for port in _router_internal_interfaces(cloud, router):
if 'fixed_ips' in port:
for fixed_ip in port['fixed_ips']:
existing_subnet_ids.append(fixed_ip['subnet_id'])
for iface in module.params['interfaces']:
if isinstance(iface, dict):
for p_id in internal_port_ids:
p = cloud.get_port(name_or_id=p_id)
if 'fixed_ips' in p:
for fip in p['fixed_ips']:
internal_subnet_ids.append(fip['subnet_id'])
if set(internal_subnet_ids) != set(existing_subnet_ids):
internal_subnet_ids = []
return True
return False
def _system_state_change(cloud, module, router, network, internal_ids, internal_portids, filters=None):
"""Check if the system state would be changed."""
state = module.params['state']
if state == 'absent' and router:
return True
if state == 'present':
if not router:
return True
return _needs_update(cloud, module, router, network, internal_ids, internal_portids, filters)
return False
def _build_kwargs(cloud, module, router, network):
kwargs = {
'admin_state_up': module.params['admin_state_up'],
}
if router:
kwargs['name_or_id'] = router['id']
else:
kwargs['name'] = module.params['name']
if network:
kwargs['ext_gateway_net_id'] = network['id']
# can't send enable_snat unless we have a network
if module.params.get('enable_snat') is not None:
kwargs['enable_snat'] = module.params['enable_snat']
if module.params['external_fixed_ips']:
kwargs['ext_fixed_ips'] = []
for iface in module.params['external_fixed_ips']:
subnet = cloud.get_subnet(iface['subnet'])
d = {'subnet_id': subnet['id']}
if 'ip' in iface:
d['ip_address'] = iface['ip']
kwargs['ext_fixed_ips'].append(d)
return kwargs
def _validate_subnets(module, cloud, filters=None):
external_subnet_ids = []
internal_subnet_ids = []
internal_port_ids = []
existing_port_ips = []
if module.params['external_fixed_ips']:
for iface in module.params['external_fixed_ips']:
subnet = cloud.get_subnet(iface['subnet'])
if not subnet:
module.fail_json(msg='subnet %s not found' % iface['subnet'])
external_subnet_ids.append(subnet['id'])
if module.params['interfaces']:
for iface in module.params['interfaces']:
if isinstance(iface, str):
subnet = cloud.get_subnet(iface, filters)
if not subnet:
module.fail_json(msg='subnet %s not found' % iface)
internal_subnet_ids.append(subnet['id'])
elif isinstance(iface, dict):
subnet = cloud.get_subnet(iface['subnet'], filters)
if not subnet:
module.fail_json(msg='subnet %s not found' % iface['subnet'])
net = cloud.get_network(iface['net'])
if not net:
module.fail_json(msg='net %s not found' % iface['net'])
if "portip" not in iface:
internal_subnet_ids.append(subnet['id'])
elif not iface['portip']:
module.fail_json(msg='put an ip in portip or remove it from list to assign default port to router')
else:
for existing_port in cloud.list_ports(filters={'network_id': net.id}):
for fixed_ip in existing_port['fixed_ips']:
if iface['portip'] == fixed_ip['ip_address']:
internal_port_ids.append(existing_port.id)
existing_port_ips.append(fixed_ip['ip_address'])
if iface['portip'] not in existing_port_ips:
p = cloud.create_port(network_id=net.id, fixed_ips=[{'ip_address': iface['portip'], 'subnet_id': subnet.id}])
if p:
internal_port_ids.append(p.id)
return external_subnet_ids, internal_subnet_ids, internal_port_ids
def main():
argument_spec = openstack_full_argument_spec(
class RouterModule(OpenStackModule):
argument_spec = dict(
state=dict(default='present', choices=['absent', 'present']),
name=dict(required=True),
admin_state_up=dict(type='bool', default=True),
@@ -386,65 +232,210 @@ def main():
project=dict(default=None)
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _router_internal_interfaces(self, router):
for port in self.conn.list_router_interfaces(router, 'internal'):
if port['device_owner'] in ROUTER_INTERFACE_OWNERS:
yield port
state = module.params['state']
name = module.params['name']
network = module.params['network']
project = module.params['project']
def _needs_update(self, router, network, internal_subnet_ids, internal_port_ids, filters=None):
"""Decide if the given router needs an update.
"""
if router['admin_state_up'] != self.params['admin_state_up']:
return True
if router['external_gateway_info']:
# check if enable_snat is set in module params
if self.params['enable_snat'] is not None:
if router['external_gateway_info'].get('enable_snat', True) != self.params['enable_snat']:
return True
if network:
if not router['external_gateway_info']:
return True
elif router['external_gateway_info']['network_id'] != network['id']:
return True
if module.params['external_fixed_ips'] and not network:
module.fail_json(msg='network is required when supplying external_fixed_ips')
# check external interfaces
if self.params['external_fixed_ips']:
for new_iface in self.params['external_fixed_ips']:
subnet = self.conn.get_subnet(new_iface['subnet'], filters)
exists = False
# compare the requested interface with existing, looking for an existing match
for existing_iface in router['external_gateway_info']['external_fixed_ips']:
if existing_iface['subnet_id'] == subnet['id']:
if 'ip' in new_iface:
if existing_iface['ip_address'] == new_iface['ip']:
# both subnet id and ip address match
exists = True
break
else:
# only the subnet was given, so ip doesn't matter
exists = True
break
# this interface isn't present on the existing router
if not exists:
return True
# check internal interfaces
if self.params['interfaces']:
existing_subnet_ids = []
for port in self._router_internal_interfaces(router):
if 'fixed_ips' in port:
for fixed_ip in port['fixed_ips']:
existing_subnet_ids.append(fixed_ip['subnet_id'])
for iface in self.params['interfaces']:
if isinstance(iface, dict):
for p_id in internal_port_ids:
p = self.conn.get_port(name_or_id=p_id)
if 'fixed_ips' in p:
for fip in p['fixed_ips']:
internal_subnet_ids.append(fip['subnet_id'])
if set(internal_subnet_ids) != set(existing_subnet_ids):
return True
return False
def _system_state_change(self, router, network, internal_ids, internal_portids, filters=None):
"""Check if the system state would be changed."""
state = self.params['state']
if state == 'absent' and router:
return True
if state == 'present':
if not router:
return True
return self._needs_update(router, network, internal_ids, internal_portids, filters)
return False
def _build_kwargs(self, router, network):
kwargs = {
'admin_state_up': self.params['admin_state_up'],
}
if router:
kwargs['name_or_id'] = router['id']
else:
kwargs['name'] = self.params['name']
if network:
kwargs['ext_gateway_net_id'] = network['id']
# can't send enable_snat unless we have a network
if self.params.get('enable_snat') is not None:
kwargs['enable_snat'] = self.params['enable_snat']
if self.params['external_fixed_ips']:
kwargs['ext_fixed_ips'] = []
for iface in self.params['external_fixed_ips']:
subnet = self.conn.get_subnet(iface['subnet'])
d = {'subnet_id': subnet['id']}
if 'ip' in iface:
d['ip_address'] = iface['ip']
kwargs['ext_fixed_ips'].append(d)
return kwargs
def _validate_subnets(self, filters=None):
external_subnet_ids = []
internal_subnet_ids = []
internal_port_ids = []
existing_port_ips = []
if self.params['external_fixed_ips']:
for iface in self.params['external_fixed_ips']:
subnet = self.conn.get_subnet(iface['subnet'])
if not subnet:
self.fail_json(msg='subnet %s not found' % iface['subnet'])
external_subnet_ids.append(subnet['id'])
if self.params['interfaces']:
for iface in self.params['interfaces']:
if isinstance(iface, str):
subnet = self.conn.get_subnet(iface, filters)
if not subnet:
self.fail(msg='subnet %s not found' % iface)
internal_subnet_ids.append(subnet['id'])
elif isinstance(iface, dict):
subnet = self.conn.get_subnet(iface['subnet'], filters)
if not subnet:
self.fail(msg='subnet %s not found' % iface['subnet'])
net = self.conn.get_network(iface['net'])
if not net:
self.fail(msg='net %s not found' % iface['net'])
if "portip" not in iface:
internal_subnet_ids.append(subnet['id'])
elif not iface['portip']:
self.fail(msg='put an ip in portip or remove it from list to assign default port to router')
else:
for existing_port in self.conn.list_ports(filters={'network_id': net.id}):
for fixed_ip in existing_port['fixed_ips']:
if iface['portip'] == fixed_ip['ip_address']:
internal_port_ids.append(existing_port.id)
existing_port_ips.append(fixed_ip['ip_address'])
if iface['portip'] not in existing_port_ips:
p = self.conn.create_port(network_id=net.id, fixed_ips=[
{
'ip_address': iface['portip'],
'subnet_id': subnet.id
}
])
if p:
internal_port_ids.append(p.id)
return external_subnet_ids, internal_subnet_ids, internal_port_ids
def run(self):
state = self.params['state']
name = self.params['name']
network = self.params['network']
project = self.params['project']
if self.params['external_fixed_ips'] and not network:
self.fail_json(msg='network is required when supplying external_fixed_ips')
sdk, cloud = openstack_cloud_from_module(module)
try:
if project is not None:
proj = cloud.get_project(project)
proj = self.conn.get_project(project)
if proj is None:
module.fail_json(msg='Project %s could not be found' % project)
self.fail(msg='Project %s could not be found' % project)
project_id = proj['id']
filters = {'tenant_id': project_id}
else:
project_id = None
filters = None
router = cloud.get_router(name, filters=filters)
router = self.conn.get_router(name, filters=filters)
net = None
if network:
net = cloud.get_network(network)
net = self.conn.get_network(network)
if not net:
module.fail_json(msg='network %s not found' % network)
self.fail(msg='network %s not found' % network)
# Validate and cache the subnet IDs so we can avoid duplicate checks
# and expensive API calls.
external_ids, subnet_internal_ids, internal_portids = _validate_subnets(module, cloud, filters)
if module.check_mode:
module.exit_json(
changed=_system_state_change(cloud, module, router, net, subnet_internal_ids, internal_portids, filters)
external_ids, subnet_internal_ids, internal_portids = self._validate_subnets(filters)
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(router, net, subnet_internal_ids, internal_portids, filters)
)
if state == 'present':
changed = False
if not router:
kwargs = _build_kwargs(cloud, module, router, net)
kwargs = self._build_kwargs(router, net)
if project_id:
kwargs['project_id'] = project_id
router = cloud.create_router(**kwargs)
router = self.conn.create_router(**kwargs)
for int_s_id in subnet_internal_ids:
cloud.add_router_interface(router, subnet_id=int_s_id)
changed = True
self.conn.add_router_interface(router, subnet_id=int_s_id)
# add interface by port id as well
for int_p_id in internal_portids:
cloud.add_router_interface(router, port_id=int_p_id)
self.conn.add_router_interface(router, port_id=int_p_id)
changed = True
else:
if _needs_update(cloud, module, router, net, subnet_internal_ids, internal_portids, filters):
kwargs = _build_kwargs(cloud, module, router, net)
updated_router = cloud.update_router(**kwargs)
if self._needs_update(router, net, subnet_internal_ids, internal_portids, filters):
kwargs = self._build_kwargs(router, net)
updated_router = self.conn.update_router(**kwargs)
# Protect against update_router() not actually
# updating the router.
@@ -455,38 +446,38 @@ def main():
# just detach all existing internal interfaces and attach the new.
if internal_portids or subnet_internal_ids:
router = updated_router
ports = _router_internal_interfaces(cloud, router)
ports = self._router_internal_interfaces(router)
for port in ports:
cloud.remove_router_interface(router, port_id=port['id'])
self.conn.remove_router_interface(router, port_id=port['id'])
if internal_portids:
external_ids, subnet_internal_ids, internal_portids = _validate_subnets(module, cloud, filters)
external_ids, subnet_internal_ids, internal_portids = self._validate_subnets(filters)
for int_p_id in internal_portids:
cloud.add_router_interface(router, port_id=int_p_id)
self.conn.add_router_interface(router, port_id=int_p_id)
changed = True
if subnet_internal_ids:
for s_id in subnet_internal_ids:
cloud.add_router_interface(router, subnet_id=s_id)
self.conn.add_router_interface(router, subnet_id=s_id)
changed = True
module.exit_json(changed=changed,
router=router,
id=router['id'])
self.exit(changed=changed, router=router, id=router['id'])
elif state == 'absent':
if not router:
module.exit_json(changed=False)
self.exit(changed=False)
else:
# We need to detach all internal interfaces on a router before
# we will be allowed to delete it.
ports = _router_internal_interfaces(cloud, router)
ports = self._router_internal_interfaces(router)
router_id = router['id']
for port in ports:
cloud.remove_router_interface(router, port_id=port['id'])
cloud.delete_router(router_id)
module.exit_json(changed=True)
self.conn.remove_router_interface(router, port_id=port['id'])
self.conn.delete_router(router_id)
self.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = RouterModule()
module()
if __name__ == '__main__':

View File

@@ -144,25 +144,30 @@ openstack_routers:
type: list
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
class RouterInfoModule(OpenStackModule):
argument_spec = openstack_full_argument_spec(
deprecated_names = ('os_routers_info', 'openstack.cloud.os_routers_info')
argument_spec = dict(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None)
)
module = AnsibleModule(argument_spec)
sdk, cloud = openstack_cloud_from_module(module)
try:
routers = cloud.search_routers(module.params['name'],
module.params['filters'])
def run(self):
kwargs = self.check_versioned(
filters=self.params['filters']
)
if self.params['name']:
kwargs['name_or_id'] = self.params['name']
routers = self.conn.search_routers(**kwargs)
for router in routers:
interfaces_info = []
for port in cloud.list_router_interfaces(router):
for port in self.conn.list_router_interfaces(router):
if port.device_owner != "network:router_gateway":
for ip_spec in port.fixed_ips:
int_info = {
@@ -173,10 +178,12 @@ def main():
interfaces_info.append(int_info)
router['interfaces_info'] = interfaces_info
module.exit_json(changed=False, openstack_routers=routers)
self.exit(changed=False, openstack_routers=routers)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = RouterInfoModule()
module()
if __name__ == '__main__':

View File

@@ -64,70 +64,61 @@ EXAMPLES = '''
project: myproj
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _needs_update(module, secgroup):
"""Check for differences in the updatable values.
class SecurityGroupModule(OpenStackModule):
NOTE: We don't currently allow name updates.
"""
if secgroup['description'] != module.params['description']:
return True
return False
def _system_state_change(module, secgroup):
state = module.params['state']
if state == 'present':
if not secgroup:
return True
return _needs_update(module, secgroup)
if state == 'absent' and secgroup:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = dict(
name=dict(required=True),
description=dict(default=''),
state=dict(default='present', choices=['absent', 'present']),
project=dict(default=None),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _needs_update(self, secgroup):
"""Check for differences in the updatable values.
name = module.params['name']
state = module.params['state']
description = module.params['description']
project = module.params['project']
NOTE: We don't currently allow name updates.
"""
if secgroup['description'] != self.params['description']:
return True
return False
def _system_state_change(self, secgroup):
state = self.params['state']
if state == 'present':
if not secgroup:
return True
return self._needs_update(secgroup)
if state == 'absent' and secgroup:
return True
return False
def run(self):
name = self.params['name']
state = self.params['state']
description = self.params['description']
project = self.params['project']
sdk, cloud = openstack_cloud_from_module(module)
try:
if project is not None:
proj = cloud.get_project(project)
proj = self.conn.get_project(project)
if proj is None:
module.fail_json(msg='Project %s could not be found' % project)
self.fail_json(msg='Project %s could not be found' % project)
project_id = proj['id']
else:
project_id = cloud.current_project_id
project_id = self.conn.current_project_id
if project_id:
filters = {'tenant_id': project_id}
else:
filters = None
secgroup = cloud.get_security_group(name, filters=filters)
secgroup = self.conn.get_security_group(name, filters=filters)
if module.check_mode:
module.exit_json(changed=_system_state_change(module, secgroup))
if self.ansible.check_mode:
self.exit(changed=self._system_state_change(secgroup))
changed = False
if state == 'present':
@@ -135,26 +126,28 @@ def main():
kwargs = {}
if project_id:
kwargs['project_id'] = project_id
secgroup = cloud.create_security_group(name, description,
**kwargs)
secgroup = self.conn.create_security_group(name, description,
**kwargs)
changed = True
else:
if _needs_update(module, secgroup):
secgroup = cloud.update_security_group(
if self._needs_update(secgroup):
secgroup = self.conn.update_security_group(
secgroup['id'], description=description)
changed = True
module.exit_json(
self.exit(
changed=changed, id=secgroup['id'], secgroup=secgroup)
if state == 'absent':
if secgroup:
cloud.delete_security_group(secgroup['id'])
self.conn.delete_security_group(secgroup['id'])
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
self.exit(changed=changed)
if __name__ == "__main__":
def main():
module = SecurityGroupModule()
module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,193 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2020 by Open Telekom Cloud, operated by T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: security_group_info
short_description: Lists security groups
extends_documentation_fragment: openstack.cloud.openstack
author: OpenStack Ansible SIG
description:
- List security groups
options:
description:
description:
- Description of the security group
type: str
name:
description:
- Name or id of the security group.
type: str
project_id:
description:
- Specifies the project id as filter criteria
type: str
revision_number:
description:
- Filter the list result by the revision number of the
- resource.
type: int
tags:
description:
- A list of tags to filter the list result by.
- Resources that match all tags in this list will be returned.
type: list
elements: str
any_tags:
description:
- A list of tags to filter the list result by.
- Resources that match any tag in this list will be returned.
type: list
elements: str
not_tags:
description:
- A list of tags to filter the list result by.
- Resources that match all tags in this list will be excluded.
type: list
elements: str
not_any_tags:
description:
- A list of tags to filter the list result by.
- Resources that match any tag in this list will be excluded.
type: list
elements: str
requirements: ["openstacksdk"]
'''
RETURN = '''
security_groups:
description: List of dictionaries describing security groups.
type: complex
returned: On Success.
contains:
created_at:
description: Creation time of the security group
type: str
sample: "yyyy-mm-dd hh:mm:ss"
description:
description: Description of the security group
type: str
sample: "My security group"
id:
description: ID of the security group
type: str
sample: "d90e55ba-23bd-4d97-b722-8cb6fb485d69"
name:
description: Name of the security group.
type: str
sample: "my-sg"
project_id:
description: Project ID where the security group is located in.
type: str
sample: "25d24fc8-d019-4a34-9fff-0a09fde6a567"
security_group_rules:
description: Specifies the security group rule list
type: list
sample: [
{
"id": "d90e55ba-23bd-4d97-b722-8cb6fb485d69",
"direction": "ingress",
"protocol": null,
"ethertype": "IPv4",
"description": null,
"remote_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2",
"remote_ip_prefix": null,
"tenant_id": "bbfe8c41dd034a07bebd592bf03b4b0c",
"port_range_max": null,
"port_range_min": null,
"security_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2"
},
{
"id": "aecff4d4-9ce9-489c-86a3-803aedec65f7",
"direction": "egress",
"protocol": null,
"ethertype": "IPv4",
"description": null,
"remote_group_id": null,
"remote_ip_prefix": null,
"tenant_id": "bbfe8c41dd034a07bebd592bf03b4b0c",
"port_range_max": null,
"port_range_min": null,
"security_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2"
}
]
updated_at:
description: Update time of the security group
type: str
sample: "yyyy-mm-dd hh:mm:ss"
'''
EXAMPLES = '''
# Get specific security group
- openstack.cloud.security_group_info:
cloud: "{{ cloud }}"
name: "{{ my_sg }}"
register: sg
# Get all security groups
- openstack.cloud.security_group_info:
cloud: "{{ cloud }}"
register: sg
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
OpenStackModule)
class SecurityGroupInfoModule(OpenStackModule):
argument_spec = dict(
description=dict(required=False, type='str'),
name=dict(required=False, type='str'),
project_id=dict(required=False, type='str'),
revision_number=dict(required=False, type='int'),
tags=dict(required=False, type='list', elements='str'),
any_tags=dict(required=False, type='list', elements='str'),
not_tags=dict(required=False, type='list', elements='str'),
not_any_tags=dict(required=False, type='list', elements='str')
)
def run(self):
description = self.params['description']
name = self.params['name']
project_id = self.params['project_id']
revision_number = self.params['revision_number']
tags = self.params['tags']
any_tags = self.params['any_tags']
not_tags = self.params['not_tags']
not_any_tags = self.params['not_any_tags']
attrs = {}
if description:
attrs['description'] = description
if project_id:
attrs['project_id'] = project_id
if revision_number:
attrs['revision_number'] = revision_number
if tags:
attrs['tags'] = ','.join(tags)
if any_tags:
attrs['any_tags'] = ','.join(any_tags)
if not_tags:
attrs['not_tags'] = ','.join(not_tags)
if not_any_tags:
attrs['not_any_tags'] = ','.join(not_any_tags)
attrs = self.check_versioned(**attrs)
result = self.conn.network.security_groups(**attrs)
result = [item if isinstance(item, dict) else item.to_dict() for item in result]
if name:
result = [item for item in result if name in (item['id'], item['name'])]
self.results.update({'security_groups': result})
def main():
module = SecurityGroupInfoModule()
module()
if __name__ == "__main__":
main()

Some files were not shown because too many files have changed in this diff Show More