122 Commits

Author SHA1 Message Date
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
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
68 changed files with 3305 additions and 2634 deletions

View File

@@ -9,6 +9,15 @@
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
@@ -103,18 +112,23 @@
vars:
tox_envlist: ansible-2.11
# Pip installation job
- job:
name: ansible-collections-openstack-functional-devstack-ansible-pip
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk with latest ansible release.
Ansible collection is built using PIP.
vars:
tox_envlist: ansible-pip
# 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
- job:
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.11
parent: ansible-collections-openstack-functional-devstack-ansible-devel
@@ -156,7 +170,6 @@
description: |
Run openstack collections functional tests against a train devstack
using train brach of openstacksdk and stable 2.11 branch of ansible
voting: false
override-checkout: stable/train
required-projects:
- name: github.com/ansible/ansible
@@ -168,6 +181,41 @@
vars:
tox_envlist: ansible
- job:
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 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.11
- name: openstack/openstacksdk
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-devel
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
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
# 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
@@ -207,41 +255,6 @@
vars:
tox_envlist: ansible
- job:
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 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.11
- name: openstack/openstacksdk
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-devel
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
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
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible-2.11
# Linters
- job:
name: openstack-tox-linters-ansible-devel
@@ -294,6 +307,58 @@
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:
@@ -306,56 +371,28 @@
- tox-pep8
- openstack-tox-linters-ansible-2.9
- openstack-tox-linters-ansible-2.11
irrelevant-files: &ignore_files
- changelogs/.*
- COPYING
- docs/.*
- .*\.md
- .*\.rst
- tools/run-ansible-sanity.sh
- tests/sanity/.*
- contrib/.*
- 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.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-victoria-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-train-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
voting: false
- ansible-collections-openstack-functional-devstack-stein-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
voting: false
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
voting: false
- ansible-collections-openstack-functional-devstack-queens-ansible-2.11:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
voting: false
- ansible-collections-openstack-functional-devstack-octavia:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- bifrost-collections-src:
voting: false
@@ -366,6 +403,13 @@
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
@@ -375,14 +419,13 @@
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.11
- ansible-collections-openstack-functional-devstack-ansible-pip
- 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-stein-ansible-2.11
# - ansible-collections-openstack-functional-devstack-rocky-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:
@@ -394,14 +437,22 @@
- ansible-collections-openstack-functional-devstack-ansible-2.9
- 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-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-stein-ansible-2.11
- ansible-collections-openstack-functional-devstack-rocky-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
pre-release:
jobs:
- ansible-collections-openstack-release

View File

@@ -5,6 +5,79 @@ 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
======

View File

@@ -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

@@ -166,3 +166,69 @@ releases:
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,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,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,6 +13,7 @@
name: "{{ image_name }}"
filename: "{{ tmp_file.stdout }}"
disk_format: raw
tags: "{{ image_tags }}"
register: image
- name: Get details of created image
@@ -25,6 +26,7 @@
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:

View File

@@ -37,6 +37,7 @@
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.8

View File

@@ -21,7 +21,7 @@
cloud: "{{ cloud }}"
name: ansible_volume_snapshot
register: snap_info
ignore_errors: sdk_version is version(0.49, '<')
ignore_errors: sdk_version is version('0.49', '<')
- name: Create volume backup
openstack.cloud.volume_backup:
@@ -30,14 +30,14 @@
display_name: ansible_volume_backup
volume: ansible_volume
register: vol_backup
ignore_errors: sdk_version is version(0.49, '<')
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, '<')
ignore_errors: sdk_version is version('0.49', '<')
- debug: var=vol

View File

@@ -4,8 +4,10 @@
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, '>=')
@@ -13,6 +15,7 @@
- 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 }

View File

@@ -33,4 +33,4 @@ build_ignore:
- ansible_collections_openstack.egg-info
- contrib
- changelogs
version: 1.4.0
version: 1.5.1-dev

View File

@@ -1,5 +1,6 @@
action_groups:
openstack:
- address_scope
- auth
- baremetal_inspect
- baremetal_inspect
@@ -20,7 +21,7 @@ action_groups:
- config
- config
- dns_zone
- dns_zone
- dns_zone_info
- endpoint
- endpoint
- federation_idp
@@ -32,6 +33,7 @@ action_groups:
- federation_mapping_info
- federation_mapping_info
- floating_ip
- floating_ip_info
- group_assignment
- group_assignment
- host_aggregate

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: False
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
@@ -112,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
@@ -159,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"

View File

@@ -120,7 +120,7 @@ def openstack_full_argument_spec(**kwargs):
auth=dict(default=None, type='dict', no_log=True),
region_name=dict(default=None),
availability_zone=dict(default=None),
validate_certs=dict(default=False, type='bool', aliases=['verify']),
validate_certs=dict(default=None, type='bool', aliases=['verify']),
ca_cert=dict(default=None, aliases=['cacert']),
client_cert=dict(default=None, aliases=['cert']),
client_key=dict(default=None, no_log=True, aliases=['key']),
@@ -256,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

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

@@ -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,26 +206,11 @@ 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'),
@@ -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,71 +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.0.0',
collection_name='openstack.cloud')
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,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,71 +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 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':
module.exit_json(changed=False)
self.exit_json(changed=False)
elif service is None and state == 'present':
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
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,53 +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.0.0',
collection_name='openstack.cloud')
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

@@ -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,62 +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.0.0',
collection_name='openstack.cloud')
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,6 +140,10 @@ 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_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule

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

@@ -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

@@ -270,10 +270,8 @@ binding:profile:
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
@@ -286,122 +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',
'binding:profile']
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 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',
'binding:profile']
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'),
@@ -421,81 +305,193 @@ def main():
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.
module.params['binding:profile'] = module.params.pop('binding_profile')
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,39 +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.0.0',
collection_name='openstack.cloud')
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 (k not in project_properties or 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,59 +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.0.0',
collection_name='openstack.cloud')
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),
@@ -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

@@ -122,29 +122,11 @@ 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, 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, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
@@ -154,85 +136,102 @@ def main():
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
)
module.module_min_sdk_version = '0.28.0'
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)
recordsets = cloud.search_recordsets(zone, name_or_id=name)
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
if recordsets:
recordset = recordsets[0]
try:
recordset_id = recordset['id']
except KeyError as e:
module.fail_json(msg=str(e))
else:
# recordsets is filtered by type and should never be more than 1 return
recordset = None
def run(self):
zone = self.params.get('zone')
name = self.params.get('name')
state = self.params.get('state')
if state == 'present':
recordset_type = module.params.get('recordset_type').upper()
records = module.params.get('records')
description = module.params.get('description')
ttl = module.params.get('ttl')
recordsets = self.conn.search_recordsets(zone, name_or_id=name)
kwargs = {}
if description:
kwargs['description'] = description
kwargs['records'] = records
if recordsets:
recordset = recordsets[0]
try:
recordset_id = recordset['id']
except KeyError as e:
self.fail_json(msg=str(e))
else:
# recordsets is filtered by type and should never be more than 1 return
recordset = None
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
records, description,
ttl, recordset))
if state == 'present':
recordset_type = self.params.get('recordset_type').upper()
records = self.params.get('records')
description = self.params.get('description')
ttl = self.params.get('ttl')
if recordset is None:
if ttl:
kwargs['ttl'] = ttl
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:
if ttl:
kwargs['ttl'] = ttl
else:
kwargs['ttl'] = 300
recordset = self.conn.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
**kwargs)
changed = True
else:
kwargs['ttl'] = 300
recordset = cloud.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
**kwargs)
changed = True
else:
if ttl:
kwargs['ttl'] = ttl
if ttl:
kwargs['ttl'] = ttl
pre_update_recordset = recordset
changed = self._system_state_change(
state, records, description, ttl, pre_update_recordset)
if changed:
recordset = self.conn.update_recordset(
zone=zone, name_or_id=recordset_id, **kwargs)
pre_update_recordset = recordset
changed = _system_state_change(state, records,
description, ttl,
pre_update_recordset)
if changed:
recordset = cloud.update_recordset(
zone=zone, name_or_id=recordset_id, **kwargs)
self.exit_json(changed=changed, recordset=recordset)
module.exit_json(changed=changed, recordset=recordset)
elif state == 'absent':
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(
state, None, None, None, recordset))
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
None, None,
None, recordset))
if recordset is None:
changed = False
else:
self.conn.delete_recordset(zone, recordset_id)
changed = True
self.exit_json(changed=changed)
if recordset is None:
changed = False
else:
cloud.delete_recordset(zone, recordset_id)
changed = True
module.exit_json(changed=changed)
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

@@ -96,70 +96,66 @@ user_id:
type: str
'''
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, server_group):
if state == 'present' and not server_group:
return True
if state == 'absent' and server_group:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class ServerGroupModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
policies=dict(required=False, type='list', elements='str'),
state=dict(default='present', choices=['absent', 'present']),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(
argument_spec,
module_kwargs = dict(
supports_check_mode=True,
**module_kwargs
)
name = module.params['name']
policies = module.params['policies']
state = module.params['state']
def _system_state_change(self, state, server_group):
if state == 'present' and not server_group:
return True
if state == 'absent' and server_group:
return True
sdk, cloud = openstack_cloud_from_module(module)
try:
server_group = cloud.get_server_group(name)
return False
if module.check_mode:
module.exit_json(
changed=_system_state_change(state, server_group)
def run(self):
name = self.params['name']
policies = self.params['policies']
state = self.params['state']
server_group = self.conn.get_server_group(name)
if self.ansible.check_mode:
self.exit_json(
changed=self._system_state_change(state, server_group)
)
changed = False
if state == 'present':
if not server_group:
if not policies:
module.fail_json(
self.fail_json(
msg="Parameter 'policies' is required in Server Group "
"Create"
)
server_group = cloud.create_server_group(name, policies)
server_group = self.conn.create_server_group(name, policies)
changed = True
module.exit_json(
self.exit_json(
changed=changed,
id=server_group['id'],
server_group=server_group
)
if state == 'absent':
if server_group:
cloud.delete_server_group(server_group['id'])
self.conn.delete_server_group(server_group['id'])
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 = ServerGroupModule()
module()
if __name__ == '__main__':

View File

@@ -93,77 +93,72 @@ metadata:
sample: {'key1': 'value1', 'key2': 'value2'}
'''
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(server_metadata=None, metadata=None):
if server_metadata is None:
server_metadata = {}
if metadata is None:
metadata = {}
return len(set(metadata.items()) - set(server_metadata.items())) != 0
def _get_keys_to_delete(server_metadata_keys=None, metadata_keys=None):
if server_metadata_keys is None:
server_metadata_keys = []
if metadata_keys is None:
metadata_keys = []
return set(server_metadata_keys) & set(metadata_keys)
def main():
argument_spec = openstack_full_argument_spec(
class ServerMetadataModule(OpenStackModule):
argument_spec = dict(
server=dict(required=True, aliases=['name']),
meta=dict(required=True, type='dict'),
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
)
state = module.params['state']
server_param = module.params['server']
meta_param = module.params['meta']
changed = False
def _needs_update(self, server_metadata=None, metadata=None):
if server_metadata is None:
server_metadata = {}
if metadata is None:
metadata = {}
return len(set(metadata.items()) - set(server_metadata.items())) != 0
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_server(server_param)
def _get_keys_to_delete(self, server_metadata_keys=None, metadata_keys=None):
if server_metadata_keys is None:
server_metadata_keys = []
if metadata_keys is None:
metadata_keys = []
return set(server_metadata_keys) & set(metadata_keys)
def run(self):
state = self.params['state']
server_param = self.params['server']
meta_param = self.params['meta']
changed = False
server = self.conn.get_server(server_param)
if not server:
module.fail_json(
self.fail_json(
msg='Could not find server {0}'.format(server_param))
if state == 'present':
# check if it needs update
if _needs_update(server_metadata=server.metadata,
metadata=meta_param):
if not module.check_mode:
cloud.set_server_metadata(server_param, meta_param)
if self._needs_update(
server_metadata=server.metadata, metadata=meta_param
):
if not self.ansible.check_mode:
self.conn.set_server_metadata(server_param, meta_param)
changed = True
elif state == 'absent':
# remove from params the keys that do not exist in the server
keys_to_delete = _get_keys_to_delete(server.metadata.keys(),
meta_param.keys())
keys_to_delete = self._get_keys_to_delete(
server.metadata.keys(), meta_param.keys())
if len(keys_to_delete) > 0:
if not module.check_mode:
cloud.delete_server_metadata(server_param, keys_to_delete)
if not self.ansible.check_mode:
self.conn.delete_server_metadata(
server_param, keys_to_delete)
changed = True
if changed:
server = cloud.get_server(server_param)
server = self.conn.get_server(server_param)
module.exit_json(
self.exit_json(
changed=changed, server_id=server.id, metadata=server.metadata)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=e.message, extra_data=e.extra_data)
def main():
module = ServerMetadataModule()
module()
if __name__ == '__main__':

View File

@@ -145,74 +145,14 @@ stack:
'updated_time': null}"
'''
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.module_utils._text import to_native
from distutils.version import StrictVersion
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def _create_stack(module, stack, cloud, sdk, parameters):
try:
stack = cloud.create_stack(module.params['name'],
template_file=module.params['template'],
environment_files=module.params['environment'],
timeout=module.params['timeout'],
wait=True,
rollback=module.params['rollback'],
**parameters)
stack = cloud.get_stack(stack.id, None)
if stack.stack_status == 'CREATE_COMPLETE':
return stack
else:
module.fail_json(msg="Failure in creating stack: {0}".format(stack))
except sdk.exceptions.OpenStackCloudException as e:
if hasattr(e, 'response'):
module.fail_json(msg=to_native(e), response=e.response.json())
else:
module.fail_json(msg=to_native(e))
def _update_stack(module, stack, cloud, sdk, parameters):
try:
stack = cloud.update_stack(
module.params['name'],
template_file=module.params['template'],
environment_files=module.params['environment'],
timeout=module.params['timeout'],
rollback=module.params['rollback'],
wait=module.params['wait'],
**parameters)
if stack['stack_status'] == 'UPDATE_COMPLETE':
return stack
else:
module.fail_json(msg="Failure in updating stack: %s" %
stack['stack_status_reason'])
except sdk.exceptions.OpenStackCloudException as e:
if hasattr(e, 'response'):
module.fail_json(msg=to_native(e), response=e.response.json())
else:
module.fail_json(msg=to_native(e))
def _system_state_change(module, stack, cloud):
state = module.params['state']
if state == 'present':
if not stack:
return True
if state == 'absent' and stack:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
class StackModule(OpenStackModule):
argument_spec = dict(
name=dict(required=True),
tag=dict(required=False, default=None),
tag=dict(required=False, default=None, min_ver='0.28.0'),
template=dict(default=None),
environment=dict(default=None, type='list', elements='str'),
parameters=dict(default={}, type='dict'),
@@ -221,53 +161,87 @@ 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
)
state = module.params['state']
name = module.params['name']
# Check for required parameters when state == 'present'
if state == 'present':
for p in ['template']:
if not module.params[p]:
module.fail_json(msg='%s required with present state' % p)
def _create_stack(self, stack, parameters):
stack = self.conn.create_stack(
self.params['name'],
template_file=self.params['template'],
environment_files=self.params['environment'],
timeout=self.params['timeout'],
wait=True,
rollback=self.params['rollback'],
**parameters)
sdk, cloud = openstack_cloud_from_module(module)
try:
stack = cloud.get_stack(name)
stack = self.conn.get_stack(stack.id, None)
if stack.stack_status == 'CREATE_COMPLETE':
return stack
else:
self.fail_json(msg="Failure in creating stack: {0}".format(stack))
if module.check_mode:
module.exit_json(changed=_system_state_change(module, stack, cloud))
def _update_stack(self, stack, parameters):
stack = self.conn.update_stack(
self.params['name'],
template_file=self.params['template'],
environment_files=self.params['environment'],
timeout=self.params['timeout'],
rollback=self.params['rollback'],
wait=self.params['wait'],
**parameters)
if stack['stack_status'] == 'UPDATE_COMPLETE':
return stack
else:
self.fail_json(msg="Failure in updating stack: %s" %
stack['stack_status_reason'])
def _system_state_change(self, stack):
state = self.params['state']
if state == 'present':
if not stack:
return True
if state == 'absent' and stack:
return True
return False
def run(self):
state = self.params['state']
name = self.params['name']
# Check for required parameters when state == 'present'
if state == 'present':
for p in ['template']:
if not self.params[p]:
self.fail_json(msg='%s required with present state' % p)
stack = self.conn.get_stack(name)
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(stack))
if state == 'present':
parameters = module.params['parameters']
if module.params['tag']:
parameters['tags'] = module.params['tag']
min_version = '0.28.0'
if StrictVersion(sdk.version.__version__) < StrictVersion(min_version) and stack:
module.warn("To update tags using openstack.cloud.stack module, the"
"installed version of the openstacksdk"
"library MUST be >={min_version}"
"".format(min_version=min_version))
parameters = self.params['parameters']
if not stack:
stack = _create_stack(module, stack, cloud, sdk, parameters)
stack = self._create_stack(stack, parameters)
else:
stack = _update_stack(module, stack, cloud, sdk, parameters)
module.exit_json(changed=True,
stack=stack,
id=stack.id)
stack = self._update_stack(stack, parameters)
self.exit_json(changed=True,
stack=stack,
id=stack.id)
elif state == 'absent':
if not stack:
changed = False
else:
changed = True
if not cloud.delete_stack(name, wait=module.params['wait']):
module.fail_json(msg='delete stack failed for stack: %s' % name)
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=to_native(e))
if not self.conn.delete_stack(name, wait=self.params['wait']):
self.fail_json(msg='delete stack failed for stack: %s' % name)
self.exit_json(changed=changed)
def main():
module = StackModule()
module()
if __name__ == '__main__':

View File

@@ -221,35 +221,37 @@ class SubnetModule(OpenStackModule):
no_gateway_ip = self.params['no_gateway_ip']
dns = self.params['dns_nameservers']
host_routes = self.params['host_routes']
curr_pool = dict(start=pool_start, end=pool_end)
if pool_start and pool_end:
pool = dict(start=pool_start, end=pool_end)
else:
pool = None
changes = dict()
if subnet['enable_dhcp'] != enable_dhcp:
return True
changes['enable_dhcp'] = enable_dhcp
if subnet_name and subnet['name'] != subnet_name:
return True
if not subnet['allocation_pools'] and pool_start and pool_end:
return True
if subnet['allocation_pools'] != [curr_pool]:
return True
changes['subnet_name'] = subnet_name
if pool and (not subnet['allocation_pools'] or subnet['allocation_pools'] != [pool]):
changes['allocation_pools'] = [pool]
if gateway_ip and subnet['gateway_ip'] != gateway_ip:
return True
changes['gateway_ip'] = gateway_ip
if dns and sorted(subnet['dns_nameservers']) != sorted(dns):
return True
changes['dns_nameservers'] = dns
if host_routes:
curr_hr = sorted(subnet['host_routes'], key=lambda t: t.keys())
new_hr = sorted(host_routes, key=lambda t: t.keys())
if curr_hr != new_hr:
return True
changes['host_routes'] = host_routes
if no_gateway_ip and subnet['gateway_ip']:
return True
return False
changes['disable_gateway_ip'] = no_gateway_ip
return changes
def _system_state_change(self, subnet, filters=None):
state = self.params['state']
if state == 'present':
if not subnet:
return True
return self._needs_update(subnet, filters)
return bool(self._needs_update(subnet, filters))
if state == 'absent' and subnet:
return True
return False
@@ -334,15 +336,9 @@ class SubnetModule(OpenStackModule):
subnet = self.conn.create_subnet(network_name, **kwargs)
changed = True
else:
if self._needs_update(subnet, filters):
subnet = self.conn.update_subnet(subnet['id'],
subnet_name=subnet_name,
enable_dhcp=enable_dhcp,
gateway_ip=gateway_ip,
disable_gateway_ip=no_gateway_ip,
dns_nameservers=dns,
allocation_pools=pool,
host_routes=host_routes)
changes = self._needs_update(subnet, filters)
if changes:
subnet = self.conn.update_subnet(subnet['id'], **changes)
changed = True
else:
changed = False

View File

@@ -188,6 +188,8 @@ class VolumeModule(OpenStackModule):
)
if self.params['image']:
image_id = self.conn.get_image_id(self.params['image'])
if not image_id:
self.fail_json(msg="Failed to find image '%s'" % self.params['image'])
volume_args['imageRef'] = image_id
if self.params['volume']:

View File

@@ -83,61 +83,11 @@ snapshot:
display_name: test_snapshot
'''
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 _present_volume_snapshot(module, cloud):
volume = cloud.get_volume(module.params['volume'])
snapshot = cloud.get_volume_snapshot(module.params['display_name'],
filters={'volume_id': volume.id})
if not snapshot:
snapshot = cloud.create_volume_snapshot(volume.id,
force=module.params['force'],
wait=module.params['wait'],
timeout=module.params[
'timeout'],
name=module.params['display_name'],
description=module.params.get(
'display_description')
)
module.exit_json(changed=True, snapshot=snapshot)
else:
module.exit_json(changed=False, snapshot=snapshot)
def _absent_volume_snapshot(module, cloud):
volume = cloud.get_volume(module.params['volume'])
snapshot = cloud.get_volume_snapshot(module.params['display_name'],
filters={'volume_id': volume.id})
if not snapshot:
module.exit_json(changed=False)
else:
cloud.delete_volume_snapshot(name_or_id=snapshot.id,
wait=module.params['wait'],
timeout=module.params['timeout'],
)
module.exit_json(changed=True, snapshot_id=snapshot.id)
def _system_state_change(module, cloud):
volume = cloud.get_volume(module.params['volume'])
snapshot = cloud.get_volume_snapshot(module.params['display_name'],
filters={'volume_id': volume.id})
state = module.params['state']
if state == 'present':
return snapshot is None
if state == 'absent':
return snapshot is not None
def main():
argument_spec = openstack_full_argument_spec(
class VolumeSnapshotModule(OpenStackModule):
argument_spec = dict(
display_name=dict(required=True, aliases=['name']),
display_description=dict(default=None, aliases=['description']),
volume=dict(required=True),
@@ -145,29 +95,72 @@ 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
)
sdk, cloud = openstack_cloud_from_module(module)
state = module.params['state']
try:
if cloud.volume_exists(module.params['volume']):
if module.check_mode:
module.exit_json(changed=_system_state_change(module, cloud))
if state == 'present':
_present_volume_snapshot(module, cloud)
if state == 'absent':
_absent_volume_snapshot(module, cloud)
def _present_volume_snapshot(self):
volume = self.conn.get_volume(self.params['volume'])
snapshot = self.conn.get_volume_snapshot(
self.params['display_name'], filters={'volume_id': volume.id})
if not snapshot:
snapshot = self.conn.create_volume_snapshot(
volume.id,
force=self.params['force'],
wait=self.params['wait'],
timeout=self.params['timeout'],
name=self.params['display_name'],
description=self.params.get('display_description')
)
self.exit_json(changed=True, snapshot=snapshot)
else:
module.fail_json(
self.exit_json(changed=False, snapshot=snapshot)
def _absent_volume_snapshot(self):
volume = self.conn.get_volume(self.params['volume'])
snapshot = self.conn.get_volume_snapshot(
self.params['display_name'], filters={'volume_id': volume.id})
if not snapshot:
self.exit_json(changed=False)
else:
self.conn.delete_volume_snapshot(
name_or_id=snapshot.id,
wait=self.params['wait'],
timeout=self.params['timeout'],
)
self.exit_json(changed=True, snapshot_id=snapshot.id)
def _system_state_change(self):
volume = self.conn.get_volume(self.params['volume'])
snapshot = self.conn.get_volume_snapshot(
self.params['display_name'],
filters={'volume_id': volume.id})
state = self.params['state']
if state == 'present':
return snapshot is None
if state == 'absent':
return snapshot is not None
def run(self):
state = self.params['state']
if self.conn.volume_exists(self.params['volume']):
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change())
if state == 'present':
self._present_volume_snapshot()
if state == 'absent':
self._absent_volume_snapshot()
else:
self.fail_json(
msg="No volume with name or id '{0}' was found.".format(
module.params['volume']))
except (sdk.exceptions.OpenStackCloudException, sdk.exceptions.ResourceTimeout) as e:
module.fail_json(msg=e.message)
self.params['volume']))
def main():
module = VolumeSnapshotModule()
module()
if __name__ == '__main__':

View File

@@ -1,12 +1,12 @@
[metadata]
name = ansible-collections-openstack.cloud
summary = Ansible collections for Openstack cloud
description-file =
description_file =
README.md
author = OpenStack
author-email = openstack-discuss@lists.openstack.org
home-page = https://opendev.org/openstack/ansible-collections-openstack/
author_email = openstack-discuss@lists.openstack.org
home_page = https://opendev.org/openstack/ansible-collections-openstack/
classifier =
License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Development Status :: 5 - Production/Stable
@@ -17,7 +17,7 @@ classifier =
Topic :: Utilities
[global]
setup-hooks =
setup_hooks =
pbr.hooks.setup_hook
[files]

10
tox.ini
View File

@@ -111,3 +111,13 @@ deps =
-r{toxinidir}/test-requirements-2.11.txt
passenv = {[testenv:ansible]passenv}
commands = {[testenv:ansible]commands}
[testenv:galaxy-release]
deps =
ansible-core
commands =
/usr/bin/mkdir -p /tmp/collection_built
/usr/bin/bash -ec 'rm -rf /tmp/collection_built/*'
/usr/bin/sed -i "s/version:.*/version: {env:VERSION_TAG}/" {toxinidir}/galaxy.yml
ansible-galaxy collection build {toxinidir} --output-path /tmp/collection_built/ --force
ansible-galaxy collection publish /tmp/collection_built/openstack-cloud-{env:VERSION_TAG}.tar.gz --token {env:API_GALAXY_TOKEN}