137 Commits
1.0.1 ... 1.3.0

Author SHA1 Message Date
Sagi Shnaidman
573e219e30 Release version 1.3.0 of Openstack Collection
Change-Id: I024e0ca9259a21956fea7f3cbd3e2c954b4dea39
2021-02-16 19:33:59 +02:00
Zuul
5c3750df2c Merge "ironic: stop putting meaningless values to properties" 2021-02-16 11:47:39 +00:00
Zuul
94e0e10e49 Merge "Allow description field to be set with os_server" 2021-02-16 10:43:53 +00:00
Zuul
db9a8d5a18 Merge "Guidelines: Fix links and formatting" 2021-02-16 09:41:03 +00:00
Zuul
7e20a8fc97 Merge "Migrating image_info module from AnsibleModule to OpenStackModule" 2021-02-16 00:43:51 +00:00
Zuul
c7969aa052 Merge "ironic: deprecate sub-options of driver_info" 2021-02-15 23:20:05 +00:00
Zuul
179a500903 Merge "Fix some typos in readme" 2021-02-15 22:23:25 +00:00
Sebastian Haderecker
ab3e136867 Guidelines: Fix links and formatting
Change-Id: I658ff486414100944b2f39d8068495cc7765fbe5
2021-02-15 20:49:37 +00:00
anbanerj
8d2391d873 Migrating image_info module from AnsibleModule to OpenStackModule
1. Added ImageInfoModule class
2. Added basic test to see if image_info reflects created image and deletion of image
3. Added deprecated_names
4. Added 'type'/'required' in argument_spec
5. Removed debug statements from test as we are checking already with image_info module

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

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

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

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

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

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

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

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

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

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

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

Ref.: 2f97394847

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

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

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

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

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

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

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

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

Story: #2008445
Task: #41412

Story: #2008444
Task: #41411

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

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

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

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

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

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

Task: 41307
Story: 2008384

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

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

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

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

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

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

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

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

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

Change-Id: I4fd36bf3ed347e020151721a6b56d1cac0a8fd23
2020-10-12 19:08:45 +02:00
Zuul
38e61994c7 Merge "Add volume_snapshot_info module" 2020-09-30 16:02:15 +00:00
Artem Goncharov
d416a27112 Add volume_snapshot_info module
Change-Id: I4edc34639f17adb97dd055fcdeec14ea92acb9bd
2020-09-30 13:40:54 +02:00
Zuul
631e1412a0 Merge "Add volume_backup_info module" 2020-09-30 09:52:35 +00:00
Zuul
4c31ea152e Merge "Make it possible to create a health monitor to a pool" 2020-09-30 07:54:37 +00:00
Artem Goncharov
a39470ac2b Add volume_backup_info module
Change-Id: I5ef76247a449b1b8653bb2bb91fccd5f3db57cf8
2020-09-30 07:38:29 +02:00
Artem Goncharov
39a8362d7a Add volume_backup module
Introduce volume_backup module to manage volume/snapshot backups.

Change-Id: Ibc4e87d47d8e38a0cf52e391dafdf025ab202982
2020-09-29 18:59:02 +02:00
Jesper Schmitz Mouridsen
05da83520e Make it possible to create a health monitor to a pool
Change-Id: I6119f5be02ace88253cba448f5a0699b39ea9ee1
2020-09-28 19:37:24 +02:00
Sagi Shnaidman
a6b52612de Fix linters for new ansible release
Limit ansible to <2.10 in 2.9 jobs,
run on ubuntu-bionic because it can provide python 3.6

Change-Id: I6d19842711f3af58449e056bee84a4c5614cd37e
2020-09-24 23:59:26 +03:00
Sagi Shnaidman
a67272d1f5 Add CI files config to bifrost jobs as well
Change-Id: Ife7bd55f44bf709319c6598c0f95201a1aac528f
2020-08-18 10:42:54 +00:00
Sagi Shnaidman
f448c78dd4 Changelog for Ansible 2.10 release
Story: #2007982
Task: #40608
Change-Id: I74219d8e7f9a8b13bbb17bc070671a1327fd5775
2020-08-17 16:04:54 +03:00
Sagi Shnaidman
33ce7ab9c7 Add volume_info module
Add module for retrieving information about volumes in a cloud.

Story: #2007817
Task: #40095
Change-Id: Ic7551c1737b08b967613e42923f5ea4ec0b606a4
2020-08-16 20:56:45 +03:00
Sagi Shnaidman
03fadf3b43 Fix non existing attribuites in SDK exception
SDK exception may not have extra_data, details or
response attributes. Print None in this case.

Change-Id: Ic4073c28a4e4afb8ca5d2b72c4ea8582da244af1
2020-08-11 14:17:36 +03:00
Zuul
86a5cc3b42 Merge "Do not require ironic_url if cloud or auth.endpoint is provided" 2020-07-28 18:06:27 +00:00
Dmitry Tantsur
8731fcc64b Do not require ironic_url if cloud or auth.endpoint is provided
The endpoint may be specified in clouds.yaml or via auth, do not
force ironic_url in either of these cases.

Finally, accept "none" as a valid no-auth plugin name.

Change-Id: I4d50b7c55727f022d79df85fb4a163fe3e5fca7b
2020-07-28 17:10:01 +02:00
Dmitry Tantsur
f6a7cf5343 Add non-voting bifrost jobs
Bifrost has fully switched to the collection, use its jobs to verify
changes. This adds two jobs: one with no-auth, one with keystone.

Depends-On: https://review.opendev.org/#/c/743167/
Change-Id: I3d46996edb4f8165a559ed70b908316e82a13353
2020-07-27 12:46:52 +02:00
Zuul
284d7871ce Merge "Add support for setting the Flavor when creating a load balancer" 2020-07-26 16:41:19 +00:00
Sagi Shnaidman
fcf6fae499 Add periodic jobs for collections
Add periodic pipeline jobs, add to it another queens jobs with
different ansible versions to increase the coverage.

Change-Id: Ie90547cb17e5c52558b6068e7128f90abffc2e25
2020-07-26 12:32:45 +00:00
Mark Chappell
88b86be33e Add support for setting the Flavor when creating a load balancer
Change-Id: I72c6b60225cacf598ddb9b7df142eced429b7226
2020-07-26 11:22:24 +00:00
Sagi Shnaidman
1f3417cdef Temporarly disable check-import
check-import has ansible as a dependency, which installs only
2.9 ansible version and it conflicts with higher ansible versions
Issue to check-import will be submitted to have ansible-test as
a dep.
Change-Id: Ide46a8a6b45677e82e57eb6a4c5dfe412d7b37fd
2020-07-26 11:49:18 +03:00
Zuul
8792b2b527 Merge "Add openstack logger and Ansible display utility" 2020-07-09 14:30:17 +00:00
Zuul
4a930cf0ec Merge "keypair: make use of OpenStackModule class" 2020-07-09 14:30:16 +00:00
Zuul
e1fe3f6067 Merge "security_group_rule: use OpenStackModule class" 2020-07-09 14:24:08 +00:00
Marc-Antoine Bourgeot
40ce8103f4 Add openstack logger and Ansible display utility
Story: 2007879

Change-Id: I76fc7df8202b4e00b54b5bafe7719e02b49e59ff
2020-07-07 22:21:47 +02:00
Baptiste Mille-Mathias
bbe1d84448 Add a link for issue report and feature request
Change-Id: I00511b314bbc8a6c03a2ee1e6c626147bfe8f49d
2020-07-04 11:31:30 +02:00
Gonéri Le Bouder
9600baec6e security_group_rule: use OpenStackModule class
Refactoring of security_group_rule to depend on the OpenStackModule
class.

See: I487e79fe18c0b9a75df7dacd224ab40ed7f4e1ab

Change-Id: Ide09bdd6a57324a0e1d2ec29c4c49db8dc1c3843
2020-07-02 19:23:15 +00:00
Gonéri Le Bouder
abebbe722c keypair: make use of OpenStackModule class
Convert the keypair module to use the OpenStackModule class.

See: I487e79fe18c0b9a75df7dacd224ab40ed7f4e1ab

Change-Id: I60cd5811f1926f53a7f88b19889fba9ca39c6184
2020-07-02 13:59:36 -04:00
Zuul
94e518e42c Merge "Update author lines" 2020-06-24 15:30:26 +00:00
Monty Taylor
a96d28dfbc Update author lines
We don't use github, so having @ mentions of specific humans is
not valuable. Also, we are a team and own the modules as a team,
so calling out individual authors is philosophically contrary.

We landed a patch upstream to special-case this author string.

Change-Id: I38b4e68f14bbba6e13e8a50e2b202874ab74e3bc
2020-06-23 21:50:43 +03:00
Artem Goncharov
32ef77d9fd Add more useful information from exception
When the module faces API exception we can give much more useful
information to the user. Let us do this for the modules inheriting from
the base class, since all modules should do this at some point in time

Change-Id: I5f1ef01765829900334aa2ecae5dab3ba96f1a49
2020-06-23 18:43:09 +00:00
Sagi Shnaidman
4b64ebe623 Fix typos in job definitions
Change-Id: I654751a879482b1996c863470a6cf68f254eefbb
2020-06-23 16:23:54 +03:00
Sagi Shnaidman
01c2499fb6 New CI jobs configuration
Change-Id: Ib6850184faf1bc0808502c098d610a5e2f41f47e
2020-06-23 12:27:04 +03:00
Sagi Shnaidman
5e3a91a7c6 Add OpenstackModule to os_server_action
Redesign the module for more OOP
Add tests for server_action
Change-Id: I054de32ee3ff34988db53fc87b1cb63b8e551ae3
2020-06-17 12:59:23 +00:00
Sagi Shnaidman
fc852da4bc Fixes for modules generation script
Change-Id: Iffbb8e9f9106860f3d896f65831d2b9283ad1e7b
2020-06-17 13:35:10 +03:00
Sagi Shnaidman
5717f05102 Move action_group to runtime
from https://github.com/openstack/ansible-collections-openstack/pull/2

Change-Id: I82c68c31bccd54d5c2624bf2081820c09791b466
2020-06-17 10:17:23 +00:00
Sagi Shnaidman
4d0df9f022 Fix ansible-test errors
Add tox to requirements
Use only 3.6 python
Set ansible-test 2.9 as a gate job

Change-Id: I40757e1efc3ee297b44cda6c35cdce4c64ebaa4f
2020-06-17 12:53:05 +03:00
Zuul
f8c768ae61 Merge "Don't pass tenant_id for remote group" 2020-06-13 16:34:06 +00:00
Sagi Shnaidman
4d1017d5d9 Add note about py3 in readme
Change-Id: I7ac15d84d54e9ebc1260b633326e19b612a83170
2020-06-11 14:24:03 +00:00
Sagi Shnaidman
c75ab0924a Fix ansible-devel sanity tests for deprecations
Change-Id: I02c3f2cb5a8bb4c891a8f0e36628925208251bf7
2020-06-11 12:55:43 +03:00
Sagi Shnaidman
dfa7983a78 Add notes to README about deps and versions
Change-Id: Ied2ac325af5aee174fc0d20d9aada67a859fa81b
2020-06-09 14:33:40 +00:00
Sagi Shnaidman
8e2e5966b7 Don't pass tenant_id for remote group
When security group is from different project, don't pass tenant
Fixes https://github.com/ansible/ansible/issues/69673
Change-Id: I230c41d1ace179390744287102fead5ddf420157
2020-06-07 18:12:30 +00:00
Sagi Shnaidman
c9da50e7e7 Add setup.py for install with pip
Make possible installation with pip.
Change-Id: I8eec015142c4f29eadff0dac2781782b76dea308
2020-06-04 14:23:23 +00:00
Sagi Shnaidman
f3610ad0e1 Redesign OpenstackModule class
don't inherit OpenstackModule class from AnsibleModule class to
prevent occasional overriding Ansible methods or vars and failing
module.

Change-Id: Ic34fff0c938eb87cc0d2c5e98fbafed64bf349f6
2020-06-04 13:49:48 +03:00
Sagi Shnaidman
7e4fbcf568 Fix ansible-tests for devel branch
Ignore for now deprecation warnings
(see https://github.com/ansible-collections/overview/issues/45#issuecomment-628262697)
Current there are bugs in ansible-test that prevent to run these
tests.

Change-Id: I9829bb23a45699e61d7b0af5ecc3e1a94bbbca85
2020-06-03 00:05:07 +03:00
Zuul
f89644973d Merge "Add template for generation of artibtrary module" 2020-05-28 19:55:17 +00:00
Zuul
b4f015ebd7 Merge "Remove unnecessary requirements file" 2020-05-25 18:12:16 +00:00
Sagi Shnaidman
f0da22da7e Remove unnecessary requirements file
for reference:
https://github.com/ansible-collections/overview/issues/43
Change-Id: I0136b4d173cb7e1d45a2b4535d8772c68f8d5783
2020-05-25 14:43:18 +03:00
Matt Parkinson
d206ea000a Minor spelling fixes in floating_ip documentation
Change-Id: Ib888b4890ff91a9bccaaa1e8b582a478243e47a3
2020-05-25 13:38:25 +10:00
Sagi Shnaidman
5667600420 Add template for generation of artibtrary module
One is for resource changing module, like server start or delete,
second one is for info collection about a specific resource.

Change-Id: I78b35075111731fff2fd50837fa4e6e0c61c55a0
2020-05-25 02:27:40 +03:00
Sagi Shnaidman
98ce765383 Exclude docs and text files form CI jobs
Change-Id: I9645d05a65d8608d7e53ae107eed1f53cca0b0e9
2020-05-24 20:44:10 +03:00
126 changed files with 6674 additions and 2371 deletions

1
.gitignore vendored
View File

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

View File

@@ -8,11 +8,16 @@
using master of openstacksdk with latest ansible release
required-projects:
- openstack/ansible-collections-openstack
- openstack/designate
vars:
zuul_work_dir: src/opendev.org/openstack/ansible-collections-openstack
tox_envlist: ansible
tox_install_siblings: true
fetch_subunit: false
devstack_plugins:
designate: https://opendev.org/openstack/designate
devstack_services:
designate: true
- job:
name: ansible-collections-openstack-functional-devstack-releases
@@ -21,142 +26,191 @@
Run openstack collections functional tests against a master devstack
using releases of openstacksdk and latest ansible release
vars:
tox_envlist: ansible
tox_install_siblings: false
# Job with Ansible 2.9 for checking backward compatibility
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.9
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and stable 2.9 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
vars:
tox_envlist: ansible-2.9
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.10
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and stable 2.10 branch of ansible
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-ansible-devel
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a master devstack
using master of openstacksdk and devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
voting: false
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
tox_envlist: ansible-2.11
# Pip installation job
- job:
name: ansible-collections-openstack-functional-devstack-ansible-2.9
parent: ansible-collections-openstack-functional-devstack-ansible-devel
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 and stable 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
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-train-ansible-devel
parent: ansible-collections-openstack-functional-devstack
name: ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a train devstack
using train brach of openstacksdk and devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
voting: false
Run openstack collections functional tests against a victoria devstack
using victoria brach of openstacksdk and stable 2.10 branch of ansible
voting: true
override-checkout: stable/victoria
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: train
- name: openstack/devstack
override-checkout: train
override-checkout: stable/victoria
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-train-ansible-2.9
name: ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a ussuri devstack
using ussuri brach of openstacksdk and stable 2.10 branch of ansible
voting: true
override-checkout: stable/ussuri
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-checkout: stable/ussuri
- name: openstack/os-client-config
override-checkout: stable/ussuri
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-train-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a train devstack
using train brach of openstacksdk and stable 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
- name: openstack/openstacksdk
override-branch: train
- name: openstack/devstack
override-checkout: train
- job:
name: ansible-collections-openstack-functional-devstack-stein-ansible-devel
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a stein devstack
using stein brach of openstacksdk and devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
using train brach of openstacksdk and stable 2.10 branch of ansible
voting: false
override-checkout: stable/train
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: stein
- name: openstack/devstack
override-checkout: stein
override-checkout: stable/train
- name: openstack/os-client-config
override-checkout: stable/train
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-stein-ansible-2.9
name: ansible-collections-openstack-functional-devstack-stein-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a stein devstack
using stein brach of openstacksdk and stable 2.9 branch of ansible
using stein brach of openstacksdk and stable 2.10 branch of ansible
voting: true
override-checkout: stable/stein
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: stein
- name: openstack/devstack
override-checkout: stein
override-checkout: stable/stein
- name: openstack/os-client-config
override-checkout: stable/stein
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-rocky-ansible-devel
parent: ansible-collections-openstack-functional-devstack
description: |
Run openstack collections functional tests against a rocky devstack
using rocky brach of openstacksdk and devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
voting: false
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
- name: openstack/openstacksdk
override-branch: rocky
- name: openstack/devstack
override-checkout: rocky
- job:
name: ansible-collections-openstack-functional-devstack-rocky-ansible-2.9
name: ansible-collections-openstack-functional-devstack-rocky-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a rocky devstack
using rocky brach of openstacksdk and stable 2.9 branch of ansible
using rocky brach of openstacksdk and stable 2.10 branch of ansible
voting: true
override-checkout: stable/rocky
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: rocky
- name: openstack/devstack
override-checkout: rocky
override-checkout: stable/rocky
- name: openstack/os-client-config
override-checkout: stable/rocky
- name: openstack/shade
override-checkout: stable/rocky
vars:
tox_envlist: ansible
- job:
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.9
name: ansible-collections-openstack-functional-devstack-queens-ansible-2.10
parent: ansible-collections-openstack-functional-devstack-ansible-devel
description: |
Run openstack collections functional tests against a queens devstack
using master brach of openstacksdk and stable 2.9 branch of ansible
using master branch of openstacksdk and stable 2.10 branch of ansible
voting: true
override-checkout: stable/queens
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
override-checkout: stable-2.10
- name: openstack/openstacksdk
override-branch: master
- name: openstack/devstack
override-checkout: queens
# Run queens with highest possible py2 version of SDK
override-checkout: stable/train
vars:
tox_envlist: ansible
- 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
parent: openstack-tox-linters
nodeset: ubuntu-bionic
description: |
Run openstack collections linter tests using the devel branch of ansible
# non-voting because we can't prevent ansible devel from breaking us
@@ -164,76 +218,148 @@
required-projects:
- name: github.com/ansible/ansible
override-checkout: devel
vars:
tox_envlist: linters-2.11
- job:
name: openstack-tox-linters-ansible-2.10
parent: openstack-tox-linters
nodeset: ubuntu-bionic
description: |
Run openstack collections linter tests using the 2.10 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.10
- job:
name: openstack-tox-linters-ansible-2.9
parent: openstack-tox-linters
nodeset: ubuntu-bionic
description: |
Run openstack collections linter tests using the 2.9 branch of ansible
voting: true
required-projects:
- name: github.com/ansible/ansible
override-checkout: stable-2.9
vars:
tox_envlist: linters-2.9
# Cross-checks with other projects
- job:
name: bifrost-collections-src
parent: bifrost-integration-tinyipa-ubuntu-focal
required-projects:
- openstack/ansible-collections-openstack
- job:
name: bifrost-keystone-collections-src
parent: bifrost-integration-tinyipa-keystone-ubuntu-focal
required-projects:
- openstack/ansible-collections-openstack
- project:
check:
jobs:
- tox-pep8
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.9
- openstack-tox-linters-ansible-2.10
# Issue with ansible-test 2.9: https://github.com/ansible/ansible/pull/73530
- openstack-tox-linters-ansible-2.9:
voting: false
- ansible-collections-openstack-functional-devstack:
dependencies: &deps_unit_lint
- tox-pep8
- openstack-tox-linters-ansible-2.9
# - openstack-tox-linters-ansible-2.9
- openstack-tox-linters-ansible-2.10
irrelevant-files: &ignore_files
- changelogs/.*
- COPYING
- docs/.*
- README.md
- .*\.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-devel:
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-train-ansible-devel:
- ansible-collections-openstack-functional-devstack-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-train-ansible-2.9:
- ansible-collections-openstack-functional-devstack-ansible-devel:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-stein-ansible-devel:
- ansible-collections-openstack-functional-devstack-ansible-pip:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-stein-ansible-2.9:
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-rocky-ansible-devel:
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.9:
- ansible-collections-openstack-functional-devstack-train-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-queens-ansible-2.9:
- ansible-collections-openstack-functional-devstack-stein-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- ansible-collections-openstack-functional-devstack-queens-ansible-2.10:
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- bifrost-collections-src:
voting: false
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
- bifrost-keystone-collections-src:
voting: false
dependencies: *deps_unit_lint
irrelevant-files: *ignore_files
gate:
jobs:
- tox-pep8
- openstack-tox-linters
- openstack-tox-linters-ansible-2.10
# https://github.com/ansible/ansible/pull/73530
# - openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-train-ansible-2.9
- ansible-collections-openstack-functional-devstack-stein-ansible-2.9
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.9
- ansible-collections-openstack-functional-devstack-queens-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.10
- ansible-collections-openstack-functional-devstack-ansible-pip
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
# - ansible-collections-openstack-functional-devstack-train-ansible-2.10
- ansible-collections-openstack-functional-devstack-stein-ansible-2.10
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.10
- ansible-collections-openstack-functional-devstack-queens-ansible-2.10
periodic:
jobs:
- openstack-tox-linters-ansible-devel
- openstack-tox-linters-ansible-2.10
- openstack-tox-linters-ansible-2.9
- ansible-collections-openstack-functional-devstack
- ansible-collections-openstack-functional-devstack-releases
- ansible-collections-openstack-functional-devstack-ansible-2.9
- ansible-collections-openstack-functional-devstack-ansible-2.10
- ansible-collections-openstack-functional-devstack-ansible-devel
- ansible-collections-openstack-functional-devstack-ansible-pip
- ansible-collections-openstack-functional-devstack-victoria-ansible-2.10
- ansible-collections-openstack-functional-devstack-ussuri-ansible-2.10
- ansible-collections-openstack-functional-devstack-train-ansible-2.10
- ansible-collections-openstack-functional-devstack-stein-ansible-2.10
- ansible-collections-openstack-functional-devstack-rocky-ansible-2.10
- ansible-collections-openstack-functional-devstack-queens-ansible-2.10
- ansible-collections-openstack-functional-devstack-queens-ansible-devel
- bifrost-collections-src
- bifrost-keystone-collections-src

182
CHANGELOG.rst Normal file
View File

@@ -0,0 +1,182 @@
=============================================
Openstack Cloud Ansilbe modules Release Notes
=============================================
.. contents:: Topics
v1.3.0
======
Release Summary
---------------
New modules and bugfixes.
Minor Changes
-------------
- Fix some typos in readme
- Guidelines Fix links and formatting
- baremetal_node - Add support for new features
- baremetal_node - ironic deprecate sub-options of driver_info
- baremetal_node - ironic stop putting meaningless values to properties
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
- recordset - Update recordset docu
- server - Allow description field to be set with os_server
- server_action - Added shelve and unshelve as new server actions
Bugfixes
--------
- port - Fixed check for None in os_port
- project - Fix setting custom property on os_project
- security_group_rule - Remove protocols choice in security rules
- volume_info - Fix volume_info result for SDK < 0.19
New Modules
-----------
- openstack.cloud.identity_role_info - Retrieve information about Openstack Identity roles.
- openstack.cloud.keypair_info - Retrieve information about Openstack key pairs.
- openstack.cloud.security_group_info - Retrieve information about Openstack Security Groups.
- openstack.cloud.security_group_rule_info - Retrieve information about Openstack Security Group rules.
- openstack.cloud.stack_info - Retrieve information about Openstack Heat stacks.
v1.2.1
======
Release Summary
---------------
Porting modules to new OpenstackModule class and fixes.
Minor Changes
-------------
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
- dns_zone, recordset - Enable update for recordset and add tests for dns and recordset module
- endpoint - Do not fail when endpoint state is absent
- ironic - Refactor ironic authentication into a new module_utils module
- loadbalancer - Refactor loadbalancer module
- network - Migrating network from AnsibleModule to OpenStackModule
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
- openstack - Add galaxy.yml to support install from git
- openstack - Fix docs-args mismatch in modules
- openstack - OpenStackModule Support defining a minimum version of the SDK
- router - Migrating routers from AnsibleModule to OpenStackModule
- routers_info - Added deprecated_names for router_info module
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
- security_group_rule - Refactor TCP/UDP port check
- server.py - Improve "server" module with OpenstackModule class
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
- subnet - Fix subnets update and idempotency
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
- volume_info - Fix volume_info arguments for SDK 0.19
v1.2.0
======
Release Summary
---------------
New volume backup modules.
Minor Changes
-------------
- lb_health_monitor - Make it possible to create a health monitor to a pool
New Modules
-----------
- openstack.cloud.volume_backup module - Add/Delete Openstack volumes backup.
- openstack.cloud.volume_backup_info module - Retrieve information about Openstack volume backups.
- openstack.cloud.volume_snapshot_info module - Retrieve information about Openstack volume snapshots.
v1.1.0
======
Release Summary
---------------
Starting redesign modules and bugfixes.
Minor Changes
-------------
- A basic module subclass was introduced and a few modules moved to inherit from it.
- Add more useful information from exception
- Added pip installation option for collection.
- Added template for generation of artibtrary module.
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is provided
- inventory_openstack - Add openstack logger and Ansible display utility
- loadbalancer - Add support for setting the Flavor when creating a load balancer
Bugfixes
--------
- Fix non existing attribuites in SDK exception
- security_group_rule - Don't pass tenant_id for remote group
New Modules
-----------
- openstack.cloud.volume_info - Retrieve information about Openstack volumes.
v1.0.1
======
Release Summary
---------------
Bugfix for server_info
Bugfixes
--------
- server_info - Fix broken server_info module and add tests
v1.0.0
======
Release Summary
---------------
Initial release of collection.
Minor Changes
-------------
- Renaming all modules and removing "os" prefix from names.
- baremetal_node_action - Support json type for the ironic_node config_drive parameter
- config - Update os_client_config to use openstacksdk
- host_aggregate - Add support for not 'purging' missing hosts
- project - Add properties for os_project
- server_action - pass imageRef to rebuild
- subnet - Updated allocation pool checks
Bugfixes
--------
- baremetal_node - Correct parameter name
- coe_cluster - Retrive id/uuid correctly
- federation_mapping - Fixup some minor nits found in followup reviews
- inventory_openstack - Fix constructed compose
- network - Bump minimum openstacksdk version when using os_network/dns_domain
- role_assignment - Fix os_user_role for groups in multidomain context
- role_assignment - Fix os_user_role issue to grant a role in a domain
New Modules
-----------
- openstack.cloud.federation_idp - Add support for Keystone Identity Providers
- openstack.cloud.federation_idp_info - Add support for fetching the information about federation IDPs
- openstack.cloud.federation_mapping - Add support for Keystone mappings
- openstack.cloud.federation_mapping_info - Add support for fetching the information about Keystone mappings
- openstack.cloud.keystone_federation_protocol - Add support for Keystone federation Protocols
- openstack.cloud.keystone_federation_protocol_info - Add support for getting information about Keystone federation Protocols
- openstack.cloud.routers_info - Retrieve information about one or more OpenStack routers.

View File

@@ -9,13 +9,37 @@ The collection includes the Openstack modules and plugins supported by Openstack
## Installation and Usage
### Installing dependencies
For using the Openstack Cloud collection firstly you need to install `ansible` and `openstacksdk` Python modules on your Ansible controller.
For example with pip:
```bash
pip install ansible openstacksdk
```
OpenStackSDK has to be available to Ansible and to the Python interpreter on the host, where Ansible executes the module (target host).
Please note, that under some circumstances Ansible might invoke a non-standard Python interpreter on the target host.
Using Python version 3 is highly recommended for OpenstackSDK and strongly required from OpenstackSDK version 0.39.0.
---
#### NOTE
OpenstackSDK is better to be the last stable version. It should NOT be installed on Openstack nodes,
but rather on operators host (aka "Ansible controller"). OpenstackSDK from last version supports
operations on all Openstack cloud versions. Therefore OpenstackSDK module version doesn't have to match
Openstack cloud version usually.
---
### Installing the Collection from Ansible Galaxy
Before using the Openstack Cloud collection, you need to install the collection with the `ansible-galaxy` CLI:
`ansible-galaxy collection install openstack.cloud`
You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml` using the format:
You can also include it in a `requirements.yml` file and install it through `ansible-galaxy collection install -r requirements.yml` using the format:
```yaml
collections:
@@ -24,7 +48,7 @@ collections:
### Playbooks
To use a module from Openstack Cloud collection, please reference the full namespace, collection name, and modules name that you want to use:
To use a module from the Openstack Cloud collection, please reference the full namespace, collection name, and module name that you want to use:
```yaml
---
@@ -42,7 +66,7 @@ To use a module from Openstack Cloud collection, please reference the full names
volume_size: 75
```
Or you can add full namepsace and collecton name in the `collections` element:
Or you can add the full namespace and collection name in the `collections` element:
```yaml
---
@@ -65,7 +89,7 @@ For information on contributing, please see [CONTRIBUTING](https://opendev.org/o
There are many ways in which you can participate in the project, for example:
- Submit bugs and feature requests, and help us verify them
- Submit [bugs and feature requests](https://storyboard.openstack.org/#!/project/openstack/ansible-collections-openstack), and help us verify them
- Submit and review source code changes in [Openstack Gerrit](https://review.opendev.org/#/q/project:openstack/ansible-collections-openstack)
- Add new modules for Openstack Cloud

151
changelogs/changelog.yaml Normal file
View File

@@ -0,0 +1,151 @@
ancestor: null
releases:
1.0.0:
changes:
bugfixes:
- baremetal_node - Correct parameter name
- coe_cluster - Retrive id/uuid correctly
- federation_mapping - Fixup some minor nits found in followup reviews
- inventory_openstack - Fix constructed compose
- network - Bump minimum openstacksdk version when using os_network/dns_domain
- role_assignment - Fix os_user_role for groups in multidomain context
- role_assignment - Fix os_user_role issue to grant a role in a domain
minor_changes:
- Renaming all modules and removing "os" prefix from names.
- baremetal_node_action - Support json type for the ironic_node config_drive
parameter
- config - Update os_client_config to use openstacksdk
- host_aggregate - Add support for not 'purging' missing hosts
- project - Add properties for os_project
- server_action - pass imageRef to rebuild
- subnet - Updated allocation pool checks
release_summary: Initial release of collection.
modules:
- description: Add support for Keystone Identity Providers
name: federation_idp
namespace: ''
- description: Add support for fetching the information about federation IDPs
name: federation_idp_info
namespace: ''
- description: Add support for Keystone mappings
name: federation_mapping
namespace: ''
- description: Add support for fetching the information about Keystone mappings
name: federation_mapping_info
namespace: ''
- description: Add support for Keystone federation Protocols
name: keystone_federation_protocol
namespace: ''
- description: Add support for getting information about Keystone federation Protocols
name: keystone_federation_protocol_info
namespace: ''
- description: Retrieve information about one or more OpenStack routers.
name: routers_info
namespace: ''
release_date: '2020-05-19'
1.0.1:
changes:
bugfixes:
- server_info - Fix broken server_info module and add tests
release_summary: Bugfix for server_info
release_date: '2020-05-22'
1.1.0:
changes:
bugfixes:
- Fix non existing attribuites in SDK exception
- security_group_rule - Don't pass tenant_id for remote group
minor_changes:
- A basic module subclass was introduced and a few modules moved to inherit
from it.
- Add more useful information from exception
- Added pip installation option for collection.
- Added template for generation of artibtrary module.
- baremetal modules - Do not require ironic_url if cloud or auth.endpoint is
provided
- inventory_openstack - Add openstack logger and Ansible display utility
- loadbalancer - Add support for setting the Flavor when creating a load balancer
release_summary: Starting redesign modules and bugfixes.
modules:
- description: Retrieve information about Openstack volumes.
name: volume_info
namespace: ''
release_date: '2020-08-17'
1.2.0:
changes:
minor_changes:
- lb_health_monitor - Make it possible to create a health monitor to a pool
release_summary: New volume backup modules.
modules:
- description: Add/Delete Openstack volumes backup.
name: volume_backup module
namespace: ''
- description: Retrieve information about Openstack volume backups.
name: volume_backup_info module
namespace: ''
- description: Retrieve information about Openstack volume snapshots.
name: volume_snapshot_info module
namespace: ''
release_date: '2020-10-13'
1.2.1:
changes:
minor_changes:
- dns_zone - Migrating dns_zone from AnsibleModule to OpenStackModule
- dns_zone, recordset - Enable update for recordset and add tests for dns and
recordset module
- endpoint - Do not fail when endpoint state is absent
- ironic - Refactor ironic authentication into a new module_utils module
- loadbalancer - Refactor loadbalancer module
- network - Migrating network from AnsibleModule to OpenStackModule
- networks_info - Migrating networks_info from AnsibleModule to OpenStackModule
- openstack - Add galaxy.yml to support install from git
- openstack - Fix docs-args mismatch in modules
- openstack - OpenStackModule Support defining a minimum version of the SDK
- router - Migrating routers from AnsibleModule to OpenStackModule
- routers_info - Added deprecated_names for router_info module
- routers_info - Migrating routers_info from AnsibleModule to OpenStackModule
- security_group.py - Migrating security_group from AnsibleModule to OpenStackModule
- security_group_rule - Refactor TCP/UDP port check
- server.py - Improve "server" module with OpenstackModule class
- server_volume - Migrating server_volume from AnsibleModule to OpenStackModule
- subnet - Fix subnets update and idempotency
- subnet - Migrating subnet module from AnsibleModule to OpenStackModule
- subnets_info - Migrating subnets_info from AnsibleModule to OpenStackModule
- volume.py - Migrating volume from AnsibleModule to OpenStackModule
- volume_info - Fix volume_info arguments for SDK 0.19
release_summary: Porting modules to new OpenstackModule class and fixes.
release_date: '2021-01-03'
1.3.0:
changes:
bugfixes:
- port - Fixed check for None in os_port
- project - Fix setting custom property on os_project
- security_group_rule - Remove protocols choice in security rules
- volume_info - Fix volume_info result for SDK < 0.19
minor_changes:
- Fix some typos in readme
- Guidelines Fix links and formatting
- baremetal_node - Add support for new features
- baremetal_node - ironic deprecate sub-options of driver_info
- baremetal_node - ironic stop putting meaningless values to properties
- image_info - Migrating image_info module from AnsibleModule to OpenStackModule
- recordset - Update recordset docu
- server - Allow description field to be set with os_server
- server_action - Added shelve and unshelve as new server actions
release_summary: New modules and bugfixes.
modules:
- description: Retrieve information about Openstack Identity roles.
name: identity_role_info
namespace: ''
- description: Retrieve information about Openstack key pairs.
name: keypair_info
namespace: ''
- description: Retrieve information about Openstack Security Groups.
name: security_group_info
namespace: ''
- description: Retrieve information about Openstack Security Group rules.
name: security_group_rule_info
namespace: ''
- description: Retrieve information about Openstack Heat stacks.
name: stack_info
namespace: ''
release_date: '2021-02-16'

31
changelogs/config.yaml Normal file
View File

@@ -0,0 +1,31 @@
changelog_filename_template: ../CHANGELOG.rst
changelog_filename_version_depth: 0
changes_file: changelog.yaml
changes_format: combined
ignore_other_fragment_extensions: true
keep_fragments: false
mention_ancestor: true
new_plugins_after_name: removed_features
notesdir: fragments
prelude_section_name: release_summary
prelude_section_title: Release Summary
sections:
- - major_changes
- Major Changes
- - minor_changes
- Minor Changes
- - breaking_changes
- Breaking Changes / Porting Guide
- - deprecated_features
- Deprecated Features
- - removed_features
- Removed Features (previously deprecated)
- - security_fixes
- Security Fixes
- - bugfixes
- Bugfixes
- - known_issues
- Known Issues
title: Openstack Cloud Ansilbe modules
trivial_section_name: trivial
use_fqcn: true

View File

View File

@@ -1,3 +0,0 @@
bugfixes:
- Bump the minimum openstacksdk version to 0.18.0 when os_network
uses the port_security_enabled or mtu arguments.

View File

@@ -1,3 +0,0 @@
bugfixes:
- Bump the minimum openstacksdk version to 0.29.0 when os_network
uses the dns_domain argument.

View File

@@ -1,2 +0,0 @@
bugfixes:
- os_coe_cluster: Retrieve the correct id/uuid depending on whether it is a create/get request.

View File

@@ -1,2 +0,0 @@
minor_changes:
- Moved Openstack inventory script from Ansible community.general to openstack collection.

View File

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

View File

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

View File

@@ -15,7 +15,16 @@
disk_format: raw
register: image
- debug: var=image
- name: Get details of created image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: image_info_result
- name: Verify image info
assert:
that:
- "image_info_result.openstack_image.name == image_name"
- name: Delete raw image (defaults)
openstack.cloud.image:
@@ -40,8 +49,6 @@
distro: ubuntu
register: image
- debug: var=image
- name: Delete raw image (complex)
openstack.cloud.image:
cloud: "{{ cloud }}"
@@ -52,3 +59,14 @@
file:
name: "{{ tmp_file.stdout }}"
state: absent
- name: Try to get details of deleted image
openstack.cloud.image_info:
cloud: "{{ cloud }}"
image: "{{ image_name }}"
register: deleted_image_info_result
- name: Verify image is deleted
assert:
that:
- not deleted_image_info_result.openstack_image

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,12 @@
- name: Get info about all servers
openstack.cloud.server_info:
cloud: "{{ cloud }}"
register: info
- name: Check info about servers
assert:
that:
info.openstack_servers|length > 0
- name: Delete server with meta as CSV
openstack.cloud.server:
@@ -25,6 +31,16 @@
name: "{{ server_name }}"
wait: true
- name: Get info about all servers
openstack.cloud.server_info:
cloud: "{{ cloud }}"
register: info
- name: Check info about no servers
assert:
that:
info.openstack_servers|length == 0
- name: Create server with meta as dict
openstack.cloud.server:
cloud: "{{ cloud }}"
@@ -46,6 +62,12 @@
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info
- name: Check info about server name
assert:
that:
info.openstack_servers[0].name == "{{ server_name }}"
- name: Delete server with meta as dict
openstack.cloud.server:
@@ -74,6 +96,12 @@
cloud: "{{ cloud }}"
server: "{{ server_name }}"
detailed: true
register: info
- name: Check info about server image name
assert:
that:
info.openstack_servers[0].image.name == "{{ image }}"
- name: Delete server (FIP from pool/network)
openstack.cloud.server:
@@ -96,14 +124,12 @@
terminate_volume: true
wait: true
register: server
tags:
- object
- debug: var=server
- name: Get info about one server in all projects
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
all_projects: true
tags:
- object
- name: Delete server with volume
openstack.cloud.server:
@@ -111,3 +137,51 @@
state: absent
name: "{{ server_name }}"
wait: true
tags:
- object
- name: Create a minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
wait: true
register: server
- debug: var=server
- name: Get info about servers in all projects
openstack.cloud.server_info:
cloud: "{{ cloud }}"
all_projects: true
register: info
- name: Check info about servers in all projects
assert:
that:
info.openstack_servers|length > 0
- name: Get info about one server in all projects
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
all_projects: true
register: info
- name: Check info about one server in all projects
assert:
that:
info.openstack_servers|length > 0
- name: Delete minimal server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: absent
name: "{{ server_name }}"
wait: true
- include_tasks: server_actions.yml

View File

@@ -0,0 +1,520 @@
- name: Create server
openstack.cloud.server:
cloud: "{{ cloud }}"
state: present
name: "{{ server_name }}"
image: "{{ image }}"
flavor: "{{ flavor }}"
network: "{{ server_network }}"
auto_floating_ip: false
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info1
- name: Ensure status for server is ACTIVE
assert:
that:
- info1.openstack_servers.0.status == 'ACTIVE'
- name: Stop server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: stop
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info2
- name: Ensure status for server is SHUTOFF
assert:
that:
- info2.openstack_servers.0.status == 'SHUTOFF'
- server is changed
- name: Stop server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: stop
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info3
- name: Ensure status for server is SHUTOFF
assert:
that:
- info3.openstack_servers.0.status == 'SHUTOFF'
- server is not changed
- name: Start server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: start
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info4
- name: Ensure status for server is ACTIVE
assert:
that:
- info4.openstack_servers.0.status == 'ACTIVE'
- server is changed
- name: Start server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: start
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info5
- name: Ensure status for server is ACTIVE
assert:
that:
- info5.openstack_servers.0.status == 'ACTIVE'
- server is not changed
- name: Pause server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: pause
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info6
- name: Ensure status for server is PAUSED
assert:
that:
- info6.openstack_servers.0.status == 'PAUSED'
- server is changed
- name: Pause server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: pause
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info7
- name: Ensure status for server is PAUSED
assert:
that:
- info7.openstack_servers.0.status == 'PAUSED'
- server is not changed
- name: Unpause server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unpause
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info8
- name: Ensure status for server is ACTIVE
assert:
that:
- info8.openstack_servers.0.status == 'ACTIVE'
- server is changed
- name: Unpause server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unpause
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info9
- name: Ensure status for server is ACTIVE
assert:
that:
- info9.openstack_servers.0.status == 'ACTIVE'
- server is not changed
- name: Lock server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: lock
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info10
- name: Ensure status for server is ACTIVE
assert:
that:
- info10.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- >-
(info10.openstack_server[0]['locked'] is defined and
info10.openstack_server[0]['locked']|bool) or
(info10.openstack_server[0]['locked'] is not defined)
- server is changed
- name: Lock server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: lock
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info11
- name: Ensure status for server is ACTIVE
assert:
that:
- info11.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- >-
(info11.openstack_server[0]['locked'] is defined and
info11.openstack_server[0]['locked']|bool) or
(info11.openstack_server[0]['locked'] is not defined)
- server is changed # no support for lock idempotency
- name: Unock server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unlock
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info12
- name: Ensure status for server is ACTIVE
assert:
that:
- info12.openstack_servers.0.status == 'ACTIVE'
# not in all versions 'locked' is supported
- >-
(info12.openstack_server[0]['locked'] is defined and
not info12.openstack_server[0]['locked']|bool) or
(info12.openstack_server[0]['locked'] is not defined)
- server is changed
- name: Unlock server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unlock
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info13
- name: Ensure status for server is ACTIVE
assert:
that:
- info13.openstack_servers.0.status == 'ACTIVE'
- server is changed # no support for unlock idempotency
# not in all versions 'locked' is supported
- >-
(info13.openstack_server[0]['locked'] is defined and
not info13.openstack_server[0]['locked']|bool) or
(info13.openstack_server[0]['locked'] is not defined)
- name: Suspend server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: suspend
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info14
- name: Ensure status for server is SUSPENDED
assert:
that:
- info14.openstack_servers.0.status == 'SUSPENDED'
- server is changed
- name: Suspend server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: suspend
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info15
- name: Ensure status for server is SUSPENDED
assert:
that:
- info15.openstack_servers.0.status == 'SUSPENDED'
- server is not changed
- name: Resume server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: resume
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info16
- name: Ensure status for server is ACTIVE
assert:
that:
- info16.openstack_servers.0.status == 'ACTIVE'
- server is changed
- name: Resume server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: resume
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info17
- name: Ensure status for server is ACTIVE
assert:
that:
- info17.openstack_servers.0.status == 'ACTIVE'
- server is not changed
- name: Rebuild server - error
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: rebuild
wait: true
register: server
ignore_errors: true
- name: Ensure server rebuild failed
assert:
that:
- server is failed
- "'missing: image' in server.msg "
- name: Rebuild server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
image: "{{ image }}"
action: rebuild
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info18
- name: Ensure status for server is ACTIVE
assert:
that:
- info18.openstack_servers.0.status in ('ACTIVE', 'REBUILD')
- server is changed
- name: Rebuild server with admin password
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
image: "{{ image }}"
action: rebuild
wait: true
admin_password: random
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info19
- name: Ensure status for server is ACTIVE
assert:
that:
- info19.openstack_servers.0.status in ('ACTIVE', 'REBUILD')
- server is changed
- name: Shelve server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info20
- name: Ensure status for server is SHELVED or SHELVED_OFFLOADED
assert:
that:
- info20.openstack_servers.0.status in ['SHELVED', 'SHELVED_OFFLOADED']
- server is changed
- name: Shelve offload server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve_offload
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info21
- name: Ensure status for server is SHELVED_OFFLOADED
# no change if server has been offloaded automatically after first shelve command
assert:
that:
- info21.openstack_servers.0.status == 'SHELVED_OFFLOADED'
- name: Shelve offload server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: shelve_offload
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info22
- name: Ensure status for server is SHELVED_OFFLOADED
assert:
that:
- info22.openstack_servers.0.status == 'SHELVED_OFFLOADED'
- server is not changed
- name: Unshelve server
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unshelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info23
- name: Ensure status for server is ACTIVE
assert:
that:
- info23.openstack_servers.0.status == 'ACTIVE'
- server is changed
- name: Unshelve server again
openstack.cloud.server_action:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
action: unshelve
wait: true
register: server
- name: Get info about server
openstack.cloud.server_info:
cloud: "{{ cloud }}"
server: "{{ server_name }}"
register: info24
- name: Ensure status for server is ACTIVE
assert:
that:
- info24.openstack_servers.0.status == 'ACTIVE'
- server is not changed

View File

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

View File

@@ -8,10 +8,63 @@
display_description: Test volume
register: vol
- name: Create volume snapshot
openstack.cloud.volume_snapshot:
cloud: "{{ cloud }}"
state: present
display_name: ansible_volume_snapshot
volume: ansible_volume
register: vol_snap
- name: Get snapshot info
openstack.cloud.volume_snapshot_info:
cloud: "{{ cloud }}"
name: ansible_volume_snapshot
register: snap_info
ignore_errors: sdk_version is version(0.49, '<')
- name: Create volume backup
openstack.cloud.volume_backup:
cloud: "{{ cloud }}"
state: present
display_name: ansible_volume_backup
volume: ansible_volume
register: vol_backup
ignore_errors: sdk_version is version(0.49, '<')
- name: Get backup info
openstack.cloud.volume_backup_info:
cloud: "{{ cloud }}"
name: ansible_volume_backup
register: backup_info
ignore_errors: sdk_version is version(0.49, '<')
- debug: var=vol
- debug: var=vol_backup
- debug: var=backup_info
- debug: var=snap_info
- name: Delete volume backup
openstack.cloud.volume_backup:
cloud: "{{ cloud }}"
display_name: ansible_volume_backup
state: absent
ignore_errors: sdk_version is version(0.49, '<')
- name: Delete volume snapshot
openstack.cloud.volume_snapshot:
cloud: "{{ cloud }}"
display_name: ansible_volume_snapshot
volume: ansible_volume
state: absent
- name: Delete volume
openstack.cloud.volume:
cloud: "{{ cloud }}"
state: absent
display_name: ansible_volume
- include_tasks: volume_info.yml

View File

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

View File

@@ -54,6 +54,16 @@ fi
shift $((OPTIND-1))
TAGS=$( echo "$*" | tr ' ' , )
# Install collections before dealing with Ansible virtual environments
if [[ -z "$PIP_INSTALL" ]]; then
tox -ebuild
ansible-galaxy collection install $(ls build_artifact/openstack-cloud-*) --force
TEST_COLLECTIONS_PATHS=${HOME}/.ansible/collections:$ANSIBLE_COLLECTIONS_PATHS
else
pip freeze | grep ansible-collections-openstack
TEST_COLLECTIONS_PATHS=$VIRTUAL_ENV/share/ansible/collections:$ANSIBLE_COLLECTIONS_PATHS
fi
# We need to source the current tox environment so that Ansible will
# be setup for the correct python environment.
source $ENVDIR/bin/activate
@@ -96,15 +106,11 @@ then
exit 1
fi
# install collections
tox -ebuild
ansible-galaxy collection build --force . --output-path ./build_artifact
ansible-galaxy collection install $(ls build_artifact/openstack-cloud-*) --force
# Discover openstackSDK version
SDK_VER=$(python -c "import openstack; print(openstack.version.__version__)")
pushd ci/
# run tests
ANSIBLE_COLLECTIONS_PATHS=${HOME}/.ansible/collections ansible-playbook \
ANSIBLE_COLLECTIONS_PATHS=$TEST_COLLECTIONS_PATHS ansible-playbook \
-vvv ./run-collection.yml \
-e "sdk_version=${SDK_VER} cloud=${CLOUD} image=${IMAGE} ${ANSIBLE_VARS}" \
${tag_opt}

View File

@@ -7,9 +7,10 @@
- { role: auth, tags: auth }
- { role: client_config, tags: client_config }
- { role: group, tags: group }
# TODO(mordred) Reenable this once the fixed openstack.cloud.image winds up in an
# upstream ansible release.
# - { role: image, tags: image }
- role: dns
tags: dns
when: sdk_version is version(0.28, '>=')
- { role: image, tags: image }
- { role: keypair, tags: keypair }
- { role: keystone_domain, tags: keystone_domain }
- role: keystone_mapping
@@ -38,3 +39,6 @@
- { role: user_group, tags: user_group }
- { role: user_role, tags: user_role }
- { role: volume, tags: volume }
- role: orchestration
tags: orchestrate
when: sdk_version is version("0.53.0", '>=')

5
contrib/generate_module.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
# For resource changing module
ansible localhost -c local -m template -a "src=module_template.py.j2 dest=my_module.py" -e @module_template_vars.yaml
# For resource info collection module
ansible localhost -c local -m template -a "src=module_info_template.py.j2 dest=my_module_info.py" -e @module_template_vars.yaml

View File

@@ -0,0 +1,110 @@
#!/usr/bin/python
# coding: utf-8 -*-
# Copyright (c) 2020, {{ author_name }} <{{ author_mail }}>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: {{ module_name }}
short_description: {{ module_short_description }}
author: OpenStack Ansible SIG
description:
- {{ module_long_description }}
options:
{{ options|to_nice_yaml(indent=2,sort_keys=false)|indent(width=2)|trim }}
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
{{ module_returns_example|to_nice_yaml(indent=2,sort_keys=false) }}
'''
EXAMPLES = '''
# What modules does for example
- {{ module_name }}:
name:
- name1
- name2
timeout: 200
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class {{ module_name.split("_")|map("capitalize")|list|join("") }}Module(OpenStackModule):
argument_spec = dict(
{% for k, v in options.items() %}
{{ k | indent( width=8, indentfirst=True) }}=dict(type='{{ v.type }}'
{%- if 'required' in v %}, required={{ v.required }}{% endif %}
{%- if 'elements' in v %}, elements={{ v.elements }}{% endif %}
{%- if 'default' in v %}, default={% if v.type == 'str' %}"{{ v.default }}"{% else %}{{ v.default }}{% endif %}{% endif %}
{%- if 'aliases' in v %}, aliases={{ v.aliases }}{% endif %}
{%- if 'choices' in v %}, choices={{ v.choices }}{% endif %}),
{% endfor %}
),
# Optional arguments requirements
module_kwargs = dict(
required_if=[
['action', 'rebuild', ['image']], # if need to rebuild image (only), the 'image' is required
["state", "present", ["username", "user_roles"]], # for creating user 'user_roles' is required
["state", "absent", ["username"]], # for state 'absent' only username is required
],
required_by=dict( # for weather and population 'city' is required to set
weather=('city'),
population=('city'),
),
mutually_exclusive=[
['use_cloud1', 'use_cloud2'] # can't run on both, choose only one to set
],
required_together=[
['remove_image', 'image_name'] # if need to remove image, must to specify which one
],
required_one_of_args=[["password", "password_hash"]], # one of these args must be set
supports_check_mode={{ check_mode_support }}, # good practice is to support check_mode
)
# you main funciton is here
def run(self):
# do any arguments check if needed
data = self.preliminary_checks()
# check if we need to prepare various filters for results
filters = self.prepare_filters()
# run SDK call to get information about requested resource
result = self.conn.compute.resource_list(
filters=filters,
detailed=self.params['detailed'],
# any other parameters
)
# process results if they require a change
result = self.normalize_result()
self.results.update({'resource_name': result})
def preliminary_checks(self):
# you checks before running like arguments and options checks, etc
return data
def prepare_filters(self):
# process filters if they require additional checks
return filters
def normalize_result(self):
# process filters if they require additional checks
return result
def main():
module = {{ module_name.split("_")|map("capitalize")|list|join("") }}Module()
module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,149 @@
#!/usr/bin/python
# coding: utf-8 -*-
# Copyright (c) 2020, {{ author_name }} <{{ author_mail }}>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: {{ module_name }}
short_description: {{ module_short_description }}
author: OpenStack Ansible SIG
description:
- {{ module_long_description }}
options:
{{ options|to_nice_yaml(indent=2,sort_keys=false)|indent(width=2)|trim }}
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
RETURN = '''
{{ module_returns_example|to_nice_yaml(indent=2,sort_keys=false) }}
'''
EXAMPLES = '''
# What modules does for example
- {{ module_name }}:
action: pause
auth:
auth_url: https://identity.example.com
username: admin
password: admin
project_name: admin
server: vm1
timeout: 200
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class {{ module_name.split("_")|map("capitalize")|list|join("") }}Module(OpenStackModule):
argument_spec = dict(
{% for k, v in options.items() %}
{{ k | indent( width=8, indentfirst=True) }}=dict(type='{{ v.type }}'
{%- if 'required' in v %}, required={{ v.required }}{% endif %}
{%- if 'elements' in v %}, elements={{ v.elements }}{% endif %}
{%- if 'default' in v %}, default={% if v.type == 'str' %}"{{ v.default }}"{% else %}{{ v.default }}{% endif %}{% endif %}
{%- if 'aliases' in v %}, aliases={{ v.aliases }}{% endif %}
{%- if 'choices' in v %}, choices={{ v.choices }}{% endif %}),
{% endfor %}
),
# Optional arguments requirements
module_kwargs = dict(
required_if=[
['action', 'rebuild', ['image']], # if need to rebuild image (only), the 'image' is required
["state", "present", ["username", "user_roles"]], # for creating user 'user_roles' is required
["state", "absent", ["username"]], # for state 'absent' only username is required
],
required_by=dict( # for weather and population 'city' is required to set
weather=('city'),
population=('city'),
),
mutually_exclusive=[
['use_cloud1', 'use_cloud2'] # can't run on both, choose only one to set
],
required_together=[
['remove_image', 'image_name'] # if need to remove image, must to specify which one
],
required_one_of_args=[["password", "password_hash"]], # one of these args must be set
supports_check_mode={{ check_mode_support }}, # good practice is to support check_mode
)
# you main funciton is here
def run(self):
# do any arguments check if needed
data = self.preliminary_checks()
# check if we need to run or the resource is in desired state already
must_run = self.check_mode_test()
# if the resource is good
if not must_run:
# updated returned results if need
self.results.update({"returning_data": some_data})
# returning {changed: False, ...} because we didn't change resource
self.exit_json(self.results)
# do something if must to run the module
self.execute()
def preliminary_checks(self):
# you checks before running like arguments and options checks, etc
return data
def check_mode_test(self):
# check the idempotency - does module should do anything or
# it's already in the desired state?
return must_run
def execute(self):
# doing here what should be done, using OpenstackSDK
# for example actions for resource:
# self.params['action'] = "rebuild"
action_name = self.params['action'] + "_resource" # action_name='rebuild_resource'
try:
# find a method "rebuild_resource" in openstack SDK compute:
func_name = getattr(self.conn.compute, action_name)
# found self.conn.compute.rebuild_resource
except AttributeError:
self.fail_json(
msg="Method %s wasn't found in OpenstackSDK compute" % action_name)
summary = func_name(data) # summary = self.conn.compute.rebuild_resource(data)
self.results.update({"returning_data": summary})
# that's it, exiting, results will be returned from module automatically
# another option for states
def execute_with_action_map(self):
actions_map = {
'start': self._start_resource,
'stop': self._stop_resource,
'restart': self._restart_resource,
'absent': self._absent_resource,
}
summary = actions_map(self.params['action'])() # summary = self.start_resource()
self.results.update({"changed": True, "data2return": summary})
def _start_resource(self, some_other_data):
pass
def _stop_resource(self, some_other_data):
pass
def _restart_resource(self, some_other_data):
pass
def _absent_resource(self, some_other_data):
pass
def main():
module = {{ module_name.split("_")|map("capitalize")|list|join("") }}Module()
module()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,81 @@
##### PLEASE READ BEFORE #####
# Module format and documentation
# https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_documenting.html#module-format-and-documentation
module_name: server_manage
author_name: 'Happy Ansible User'
author_mail: dontwriteme@example.com
module_short_description: "Doing something very useful"
module_long_description: "Here is the place to release your inner writer"
check_mode_support: True # good practice to support check_mode:
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html#check-mode-dry-run
module_returns_example:
image:
description: Image inspection results for the image that was pulled, pushed, or built.
returned: always # or 'success' in case of success only
type: dict
sample:
Image Name: Sample Image
Image ID: e6471d00796a13de8142c15d7ad3a44f
Nested:
images list:
- data 1
- image 1234
boolean_1: True
options:
optional_string:
description:
- This variable is set for having string argument, for example 'action'
type: str
required: true
default: "my_lovely_action"
choices:
- allowed_option1
- allowed_option1
optional_boolean:
description:
- This variable is set for having a boolean argument, for example whether
to wait for resource creation or not
type: bool
required: false # may be omitted if false
# and no default because not required
optional_integer:
description:
- This variable is set for having a integer argument, for example how many
seconds to wait for the resource to come alive
required: true
default: 60
type: int
aliases: # sometimes we allow to pass the same option with different name
- old_optional_integer_name
- different_option_name
optional_list:
description:
- This variable is set for having a list argument, for example files need
to create with the resource
type: list
elements: str # type of elements of the list, can be dict, str, int, list
optional_dictionary:
description:
- This variable is set for having a dictionary argument, for example to
set environment variables or to pass more complex data to SDK
required: true
default: {}
type: dict
suboptions:
suboption_1:
description:
- suboption_1 description, what it does
type: str
aliases:
- suboption_1_another_name
suboption_2:
description:
- suboption_2 description, what it does
type: list
elements: str
default: []

View File

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

36
galaxy.yml Normal file
View File

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

View File

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

View File

@@ -1,269 +0,0 @@
openstack:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_rule
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_snapshot
os:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_rule
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_snapshot
- os_auth
- os_client_config
- os_client_config
- os_coe_cluster
- os_coe_cluster_template
- os_endpoint
- os_flavor
- os_flavor_info
- os_flavor_info
- os_floating_ip
- os_group
- os_group
- os_group_info
- os_group_info
- os_image
- os_image_info
- os_ironic
- os_ironic
- os_ironic_inspect
- os_ironic_inspect
- os_ironic_node
- os_ironic_node
- os_keypair
- os_keystone_domain
- os_keystone_domain
- os_keystone_domain_info
- os_keystone_domain_info
- os_keystone_endpoint
- os_keystone_endpoint
- os_keystone_federation_protocol
- os_keystone_federation_protocol_info
- os_keystone_identity_provider
- os_keystone_identity_provider
- os_keystone_identity_provider_info
- os_keystone_identity_provider_info
- os_keystone_mapping
- os_keystone_mapping
- os_keystone_mapping_info
- os_keystone_mapping_info
- os_keystone_role
- os_keystone_role
- os_keystone_service
- os_keystone_service
- os_listener
- os_listener
- os_loadbalancer
- os_member
- os_member
- os_network
- os_networks_info
- os_nova_flavor
- os_nova_flavor
- os_nova_host_aggregate
- os_nova_host_aggregate
- os_object
- os_pool
- os_pool
- os_port
- os_port_info
- os_project
- os_project_access
- os_project_info
- os_quota
- os_recordset
- os_router
- os_routers_info
- os_security_group
- os_security_group_rule
- os_server
- os_server_action
- os_server_group
- os_server_info
- os_server_metadata
- os_server_volume
- os_stack
- os_subnet
- os_subnets_info
- os_user
- os_user
- os_user_group
- os_user_group
- os_user_info
- os_user_info
- os_user_role
- os_user_role
- os_volume
- os_volume_snapshot
- os_zone
- os_zone

View File

@@ -1,317 +1,602 @@
action_groups:
openstack:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keypair_info
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_info
- security_group_rule
- security_group_rule_info
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_backup
- volume_backup_info
- volume_info
- volume_snapshot
- volume_snapshot_info
os:
- auth
- baremetal_inspect
- baremetal_inspect
- baremetal_node
- baremetal_node
- baremetal_node_action
- baremetal_node_action
- catalog_endpoint
- catalog_service
- catalog_service
- coe_cluster
- coe_cluster_template
- compute_flavor
- compute_flavor
- compute_flavor
- compute_flavor_info
- compute_flavor_info
- config
- config
- dns_zone
- dns_zone
- endpoint
- endpoint
- federation_idp
- federation_idp
- federation_idp_info
- federation_idp_info
- federation_mapping
- federation_mapping
- federation_mapping_info
- federation_mapping_info
- floating_ip
- group_assignment
- group_assignment
- host_aggregate
- host_aggregate
- identity_domain
- identity_domain
- identity_domain_info
- identity_domain_info
- identity_group
- identity_group
- identity_group_info
- identity_group_info
- identity_role
- identity_role
- identity_user
- identity_user
- identity_user_info
- identity_user_info
- image
- image_info
- keypair
- keypair_info
- keystone_federation_protocol
- keystone_federation_protocol_info
- lb_listener
- lb_listener
- lb_member
- lb_member
- lb_pool
- lb_pool
- loadbalancer
- network
- networks_info
- object
- port
- port_info
- project
- project_access
- project_info
- quota
- recordset
- role_assignment
- role_assignment
- router
- routers_info
- security_group
- security_group_info
- security_group_rule
- security_group_rule_info
- server
- server_action
- server_group
- server_info
- server_metadata
- server_volume
- stack
- subnet
- subnets_info
- volume
- volume_backup
- volume_backup_info
- volume_info
- volume_snapshot
- volume_snapshot_info
- os_auth
- os_client_config
- os_client_config
- os_coe_cluster
- os_coe_cluster_template
- os_endpoint
- os_flavor
- os_flavor_info
- os_flavor_info
- os_floating_ip
- os_group
- os_group
- os_group_info
- os_group_info
- os_image
- os_image_info
- os_ironic
- os_ironic
- os_ironic_inspect
- os_ironic_inspect
- os_ironic_node
- os_ironic_node
- os_keypair
- os_keystone_domain
- os_keystone_domain
- os_keystone_domain_info
- os_keystone_domain_info
- os_keystone_endpoint
- os_keystone_endpoint
- os_keystone_federation_protocol
- os_keystone_federation_protocol_info
- os_keystone_identity_provider
- os_keystone_identity_provider
- os_keystone_identity_provider_info
- os_keystone_identity_provider_info
- os_keystone_mapping
- os_keystone_mapping
- os_keystone_mapping_info
- os_keystone_mapping_info
- os_keystone_role
- os_keystone_role
- os_keystone_service
- os_keystone_service
- os_listener
- os_listener
- os_loadbalancer
- os_member
- os_member
- os_network
- os_networks_info
- os_nova_flavor
- os_nova_flavor
- os_nova_host_aggregate
- os_nova_host_aggregate
- os_object
- os_pool
- os_pool
- os_port
- os_port_info
- os_project
- os_project_access
- os_project_info
- os_quota
- os_recordset
- os_router
- os_routers_info
- os_security_group
- os_security_group_rule
- os_server
- os_server_action
- os_server_group
- os_server_info
- os_server_metadata
- os_server_volume
- os_stack
- os_subnet
- os_subnets_info
- os_user
- os_user
- os_user_group
- os_user_group
- os_user_info
- os_user_info
- os_user_role
- os_user_role
- os_volume
- os_volume_snapshot
- os_zone
- os_zone
plugin_routing:
modules:
os_auth:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.auth
redirect: openstack.cloud.auth
os_client_config:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.config
redirect: openstack.cloud.config
os_coe_cluster:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.coe_cluster
redirect: openstack.cloud.coe_cluster
os_coe_cluster_template:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.coe_cluster_template
redirect: openstack.cloud.coe_cluster_template
os_endpoint:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.catalog_endpoint
redirect: openstack.cloud.catalog_endpoint
os_flavor:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.compute_flavor
redirect: openstack.cloud.compute_flavor
os_flavor_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.compute_flavor_info
redirect: openstack.cloud.compute_flavor_info
os_floating_ip:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.floating_ip
redirect: openstack.cloud.floating_ip
os_group:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_group
redirect: openstack.cloud.identity_group
os_group_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_group_info
redirect: openstack.cloud.identity_group_info
os_image:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.image
redirect: openstack.cloud.image
os_image_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.image_info
redirect: openstack.cloud.image_info
os_ironic:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.baremetal_node
redirect: openstack.cloud.baremetal_node
os_ironic_inspect:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.baremetal_inspect
redirect: openstack.cloud.baremetal_inspect
os_ironic_node:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.baremetal_node_action
redirect: openstack.cloud.baremetal_node_action
os_keypair:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.keypair
redirect: openstack.cloud.keypair
os_keystone_domain:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_domain
redirect: openstack.cloud.identity_domain
os_keystone_domain_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_domain_info
redirect: openstack.cloud.identity_domain_info
os_keystone_endpoint:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.endpoint
redirect: openstack.cloud.endpoint
os_keystone_federation_protocol:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.keystone_federation_protocol
redirect: openstack.cloud.keystone_federation_protocol
os_keystone_federation_protocol_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.keystone_federation_protocol_info
redirect: openstack.cloud.keystone_federation_protocol_info
os_keystone_identity_provider:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.federation_idp
redirect: openstack.cloud.federation_idp
os_keystone_identity_provider_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.federation_idp_info
redirect: openstack.cloud.federation_idp_info
os_keystone_mapping:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.federation_mapping
redirect: openstack.cloud.federation_mapping
os_keystone_mapping_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.federation_mapping_info
redirect: openstack.cloud.federation_mapping_info
os_keystone_role:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_role
redirect: openstack.cloud.identity_role
os_keystone_service:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.catalog_service
redirect: openstack.cloud.catalog_service
os_listener:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.lb_listener
redirect: openstack.cloud.lb_listener
os_loadbalancer:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.loadbalancer
redirect: openstack.cloud.loadbalancer
os_member:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.lb_member
redirect: openstack.cloud.lb_member
os_network:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.network
redirect: openstack.cloud.network
os_networks_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.networks_info
redirect: openstack.cloud.networks_info
os_nova_flavor:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.compute_flavor
redirect: openstack.cloud.compute_flavor
os_nova_host_aggregate:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.host_aggregate
redirect: openstack.cloud.host_aggregate
os_object:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.object
redirect: openstack.cloud.object
os_pool:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.lb_pool
redirect: openstack.cloud.lb_pool
os_port:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.port
redirect: openstack.cloud.port
os_port_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.port_info
redirect: openstack.cloud.port_info
os_project:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.project
redirect: openstack.cloud.project
os_project_access:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.project_access
redirect: openstack.cloud.project_access
os_project_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.project_info
redirect: openstack.cloud.project_info
os_quota:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.quota
redirect: openstack.cloud.quota
os_recordset:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.recordset
redirect: openstack.cloud.recordset
os_router:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.router
redirect: openstack.cloud.router
os_routers_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.routers_info
redirect: openstack.cloud.routers_info
os_security_group:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.security_group
redirect: openstack.cloud.security_group
os_security_group_rule:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.security_group_rule
redirect: openstack.cloud.security_group_rule
os_server:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server
redirect: openstack.cloud.server
os_server_action:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server_action
redirect: openstack.cloud.server_action
os_server_group:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server_group
redirect: openstack.cloud.server_group
os_server_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server_info
redirect: openstack.cloud.server_info
os_server_metadata:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server_metadata
redirect: openstack.cloud.server_metadata
os_server_volume:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.server_volume
redirect: openstack.cloud.server_volume
os_stack:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.stack
redirect: openstack.cloud.stack
os_subnet:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.subnet
redirect: openstack.cloud.subnet
os_subnets_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.subnets_info
redirect: openstack.cloud.subnets_info
os_user:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_user
redirect: openstack.cloud.identity_user
os_user_group:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.group_assignment
redirect: openstack.cloud.group_assignment
os_user_info:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.identity_user_info
redirect: openstack.cloud.identity_user_info
os_user_role:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.role_assignment
redirect: openstack.cloud.role_assignment
os_volume:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.volume
redirect: openstack.cloud.volume
os_volume_snapshot:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.volume_snapshot
redirect: openstack.cloud.volume_snapshot
os_zone:
deprecation:
removal_date: TBD
removal_date: 2021-12-12
warning_text: os_ prefixed module names are deprecated, use openstack.cloud.dns_zone
redirect: openstack.cloud.dns_zone

View File

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

View File

@@ -10,9 +10,7 @@ DOCUMENTATION = '''
---
name: openstack
plugin_type: inventory
author:
- "Marco Vito Moscaritolo <marco@agavee.com>"
- "Jesse Keating <jesse.keating@rackspace.com>"
author: OpenStack Ansible SIG
short_description: OpenStack inventory source
requirements:
- "openstacksdk >= 0.28"
@@ -118,9 +116,14 @@ all_projects: yes
import collections
import sys
import logging
from ansible.errors import AnsibleParserError
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
from ansible.utils.display import Display
display = Display()
os_logger = logging.getLogger("openstack")
try:
# Due to the name shadowing we should import other way
@@ -128,8 +131,10 @@ try:
sdk = importlib.import_module('openstack')
sdk_inventory = importlib.import_module('openstack.cloud.inventory')
client_config = importlib.import_module('openstack.config.loader')
sdk_exceptions = importlib.import_module("openstack.exceptions")
HAS_SDK = True
except ImportError:
display.vvvv("Couldn't import Openstack SDK modules")
HAS_SDK = False
@@ -158,11 +163,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
msg = "openstacksdk is required for the OpenStack inventory plugin. OpenStack inventory sources will be skipped."
if msg:
display.vvvv(msg)
raise AnsibleParserError(msg)
# The user has pointed us at a clouds.yaml file. Use defaults for
# everything.
if 'clouds' in self._config_data:
self.display.vvvv(
"Found clouds config file instead of plugin config. "
"Using default configuration."
)
self._config_data = {}
# update cache if the user has caching enabled and the cache is being refreshed
@@ -173,13 +181,16 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
cache = self.get_option('cache')
source_data = None
if cache:
self.display.vvvv("Reading inventory data from cache: %s" % cache_key)
try:
source_data = self._cache[cache_key]
except KeyError:
# cache expired or doesn't exist yet
display.vvvv("Inventory data cache not found")
cache_needs_update = True
if not source_data:
self.display.vvvv("Getting hosts from Openstack clouds")
clouds_yaml_path = self._config_data.get('clouds_yaml_path')
if clouds_yaml_path:
config_files = (
@@ -192,11 +203,16 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
# Redict logging to stderr so it does not mix with output
# particular ansible-inventory JSON output
# TODO(mordred) Integrate openstack's logging with ansible's logging
sdk.enable_logging(stream=sys.stderr)
if self.display.verbosity > 3:
sdk.enable_logging(debug=True, stream=sys.stderr)
else:
sdk.enable_logging(stream=sys.stderr)
cloud_inventory = sdk_inventory.OpenStackInventory(
config_files=config_files,
private=self._config_data.get('private', False))
self.display.vvvv("Found %d cloud(s) in Openstack" %
len(cloud_inventory.clouds))
only_clouds = self._config_data.get('only_clouds', [])
if only_clouds and not isinstance(only_clouds, list):
raise ValueError(
@@ -205,20 +221,31 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
if only_clouds:
new_clouds = []
for cloud in cloud_inventory.clouds:
self.display.vvvv("Looking at cloud : %s" % cloud.name)
if cloud.name in only_clouds:
self.display.vvvv("Selecting cloud : %s" % cloud.name)
new_clouds.append(cloud)
cloud_inventory.clouds = new_clouds
self.display.vvvv("Selected %d cloud(s)" %
len(cloud_inventory.clouds))
expand_hostvars = self._config_data.get('expand_hostvars', False)
fail_on_errors = self._config_data.get('fail_on_errors', False)
all_projects = self._config_data.get('all_projects', False)
source_data = cloud_inventory.list_hosts(
expand=expand_hostvars, fail_on_cloud_config=fail_on_errors,
all_projects=all_projects)
if cache_needs_update:
self._cache[cache_key] = source_data
source_data = []
try:
source_data = cloud_inventory.list_hosts(
expand=expand_hostvars, fail_on_cloud_config=fail_on_errors,
all_projects=all_projects)
except Exception as e:
self.display.warning("Couldn't list Openstack hosts. "
"See logs for details")
os_logger.error(e.message)
finally:
if cache_needs_update:
self._cache[cache_key] = source_data
self._populate_from_source(source_data)
@@ -344,5 +371,6 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
for suffix in ('yaml', 'yml'):
maybe = '{fn}.{suffix}'.format(fn=fn, suffix=suffix)
if path.endswith(maybe):
self.display.vvvv("Valid plugin config file found")
return True
return False

View File

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

View File

@@ -28,6 +28,7 @@
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import abc
import copy
from distutils.version import StrictVersion
import importlib
import os
@@ -35,6 +36,39 @@ import os
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
OVERRIDES = {'os_client_config': 'config',
'os_endpoint': 'catalog_endpoint',
'os_flavor': 'compute_flavor',
'os_flavor_info': 'compute_flavor_info',
'os_group': 'identity_group',
'os_group_info': 'identity_group_info',
'os_ironic': 'baremetal_node',
'os_ironic_inspect': 'baremetal_inspect',
'os_ironic_node': 'baremetal_node_action',
'os_keystone_domain': 'identity_domain',
'os_keystone_domain_info': 'identity_domain_info',
'os_keystone_endpoint': 'endpoint',
'os_keystone_identity_provider': 'federation_idp',
'os_keystone_identity_provider_info': 'federation_idp_info',
'os_keystone_mapping': 'federation_mapping',
'os_keystone_mapping_info': 'federation_mapping_info',
'os_keystone_role': 'identity_role',
'os_keystone_service': 'catalog_service',
'os_listener': 'lb_listener',
'os_member': 'lb_member',
'os_nova_flavor': 'compute_flavor',
'os_nova_host_aggregate': 'host_aggregate',
'os_pool': 'lb_pool',
'os_user': 'identity_user',
'os_user_group': 'group_assignment',
'os_user_info': 'identity_user_info',
'os_user_role': 'role_assignment',
'os_zone': 'dns_zone'}
CUSTOM_VAR_PARAMS = ['min_ver', 'max_ver']
MINIMUM_SDK_VERSION = '0.12.0'
def openstack_argument_spec():
# DEPRECATED: This argument spec is only used for the deprecated old
@@ -86,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=None, type='bool', aliases=['verify']),
validate_certs=dict(default=False, 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']),
@@ -97,7 +131,12 @@ def openstack_full_argument_spec(**kwargs):
default='public', choices=['public', 'internal', 'admin'],
aliases=['endpoint_type']),
)
spec.update(kwargs)
# Filter out all our custom parameters before passing to AnsibleModule
kwargs_copy = copy.deepcopy(kwargs)
for v in kwargs_copy.values():
for c in CUSTOM_VAR_PARAMS:
v.pop(c, None)
spec.update(kwargs_copy)
return spec
@@ -109,11 +148,11 @@ def openstack_module_kwargs(**kwargs):
ret[key].extend(kwargs[key])
else:
ret[key] = kwargs[key]
return ret
def openstack_cloud_from_module(module, min_version='0.12.0'):
# for compatibility with old versions
def openstack_cloud_from_module(module, min_version=None):
try:
# Due to the name shadowing we should import other way
sdk = importlib.import_module('openstack')
@@ -122,9 +161,10 @@ def openstack_cloud_from_module(module, min_version='0.12.0'):
module.fail_json(msg='openstacksdk is required for this module')
if min_version:
min_version = max(StrictVersion('0.12.0'), StrictVersion(min_version))
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(min_version))
else:
min_version = StrictVersion('0.12.0')
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(sdk_version.__version__) < min_version:
module.fail_json(
@@ -166,25 +206,215 @@ def openstack_cloud_from_module(module, min_version='0.12.0'):
module.fail_json(msg=str(e))
class OpenStackModule(AnsibleModule):
class OpenStackModule:
"""Openstack Module is a base class for all Openstack Module classes.
The class has `run` function that should be overriden in child classes,
the provided methods include:
Methods:
params: Dictionary of Ansible module parameters.
module_name: Module name (i.e. server_action)
sdk_version: Version of used OpenstackSDK.
results: Dictionary for return of Ansible module,
must include `changed` keyword.
exit, exit_json: Exit module and return data inside, must include
changed` keyword in a data.
fail, fail_json: Exit module with failure, has `msg` keyword to
specify a reason of failure.
conn: Connection to SDK object.
log: Print message to system log.
debug: Print debug message to system log, prints if Ansible Debug is
enabled or verbosity is more than 2.
check_deprecated_names: Function that checks if module was called with
a deprecated name and prints the correct name
with deprecation warning.
check_versioned: helper function to check that all arguments are known
in the current SDK version.
run: method that executes and shall be overriden in inherited classes.
Args:
deprecated_names: Should specify deprecated modules names for current
module.
argument_spec: Used for construction of Openstack common arguments.
module_kwargs: Additional arguments for Ansible Module.
"""
deprecated_names = ()
argument_spec = {}
module_kwargs = {}
module_min_sdk_version = None
def __init__(self):
"""Initialize Openstack base class.
super(OpenStackModule, self).__init__(
Set up variables, connection to SDK and check if there are
deprecated names.
"""
self.ansible = AnsibleModule(
openstack_full_argument_spec(**self.argument_spec),
**self.module_kwargs)
self.params = self.ansible.params
self.module_name = self.ansible._name
self.sdk_version = None
self.results = {'changed': False}
self.exit = self.exit_json = self.ansible.exit_json
self.fail = self.fail_json = self.ansible.fail_json
self.sdk, self.conn = self.openstack_cloud_from_module()
self.check_deprecated_names()
self.sdk, self.conn = openstack_cloud_from_module(self)
def log(self, msg):
"""Prints log message to system log.
Arguments:
msg {str} -- Log message
"""
self.ansible.log(msg)
def debug(self, msg):
"""Prints debug message to system log
Arguments:
msg {str} -- Debug message.
"""
if self.ansible._debug or self.ansible._verbosity > 2:
self.ansible.log(
" ".join(['[DEBUG]', msg]))
def check_deprecated_names(self):
"""Check deprecated module names if `deprecated_names` variable is set.
"""
new_module_name = OVERRIDES.get(self.module_name)
if self.module_name in self.deprecated_names and new_module_name:
self.ansible.deprecate(
"The '%s' module has been renamed to '%s' in openstack "
"collection: openstack.cloud.%s" % (
self.module_name, new_module_name, new_module_name),
version='2.10')
def openstack_cloud_from_module(self):
"""Sets up connection to cloud using provided options. Checks if all
provided variables are supported for the used SDK version.
"""
try:
# Due to the name shadowing we should import other way
sdk = importlib.import_module('openstack')
sdk_version_lib = importlib.import_module('openstack.version')
self.sdk_version = sdk_version_lib.__version__
except ImportError:
self.fail_json(msg='openstacksdk is required for this module')
# Fail if the available SDK version doesn't meet the minimum version
# requirements
if self.module_min_sdk_version:
min_version = max(StrictVersion(MINIMUM_SDK_VERSION),
StrictVersion(self.module_min_sdk_version))
else:
min_version = StrictVersion(MINIMUM_SDK_VERSION)
if StrictVersion(self.sdk_version) < min_version:
self.fail(
msg="To utilize this module, the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=min_version))
# Fail if there are set unsupported for this version parameters
# New parameters should NOT use 'default' but rely on SDK defaults
for param in self.argument_spec:
if (self.params[param] is not None
and 'min_ver' in self.argument_spec[param]
and StrictVersion(self.sdk_version) < self.argument_spec[param]['min_ver']):
self.fail_json(
msg="To use parameter '{param}' with module '{module}', the installed version of "
"the openstacksdk library MUST be >={min_version}.".format(
min_version=self.argument_spec[param]['min_ver'],
param=param,
module=self.module_name))
if (self.params[param] is not None
and 'max_ver' in self.argument_spec[param]
and StrictVersion(self.sdk_version) > self.argument_spec[param]['max_ver']):
self.fail_json(
msg="To use parameter '{param}' with module '{module}', the installed version of "
"the openstacksdk library MUST be <={max_version}.".format(
max_version=self.argument_spec[param]['max_ver'],
param=param,
module=self.module_name))
cloud_config = self.params.pop('cloud', None)
if isinstance(cloud_config, dict):
fail_message = (
"A cloud config dict was provided to the cloud parameter"
" but also a value was provided for {param}. If a cloud"
" config dict is provided, {param} should be"
" excluded.")
for param in (
'auth', 'region_name', 'validate_certs',
'ca_cert', 'client_key', 'api_timeout', 'auth_type'):
if self.params[param] is not None:
self.fail_json(msg=fail_message.format(param=param))
# For 'interface' parameter, fail if we receive a non-default value
if self.params['interface'] != 'public':
self.fail_json(msg=fail_message.format(param='interface'))
else:
cloud_config = dict(
cloud=cloud_config,
auth_type=self.params['auth_type'],
auth=self.params['auth'],
region_name=self.params['region_name'],
verify=self.params['validate_certs'],
cacert=self.params['ca_cert'],
key=self.params['client_key'],
api_timeout=self.params['api_timeout'],
interface=self.params['interface'],
)
try:
return sdk, sdk.connect(**cloud_config)
except sdk.exceptions.SDKException as e:
# Probably a cloud configuration/login error
self.fail_json(msg=str(e))
# Filter out all arguments that are not from current SDK version
def check_versioned(self, **kwargs):
"""Check that provided arguments are supported by current SDK version
Returns:
versioned_result {dict} dictionary of only arguments that are
supported by current SDK version. All others
are dropped.
"""
versioned_result = {}
for var_name in kwargs:
if ('min_ver' in self.argument_spec[var_name]
and StrictVersion(self.sdk_version) < self.argument_spec[var_name]['min_ver']):
continue
if ('max_ver' in self.argument_spec[var_name]
and StrictVersion(self.sdk_version) > self.argument_spec[var_name]['max_ver']):
continue
versioned_result.update({var_name: kwargs[var_name]})
return versioned_result
@abc.abstractmethod
def run(self):
"""Function for overriding in inhetired classes, it's executed by default.
"""
pass
def __call__(self):
"""Execute `run` function when calling the class.
"""
try:
self.run()
results = self.run()
if results and isinstance(results, dict):
self.ansible.exit_json(**results)
except self.sdk.exceptions.OpenStackCloudException as e:
self.fail_json(msg=str(e), extra_data=e.extra_data)
params = {
'msg': str(e),
'extra_data': {
'data': getattr(e, 'extra_data', 'None'),
'details': getattr(e, 'details', 'None'),
'response': getattr(getattr(e, 'response', ''),
'text', 'None')
}
}
self.ansible.fail_json(**params)
# if we got to this place, modules didn't exit
self.ansible.exit_json(**self.results)

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: auth
short_description: Retrieve an auth token
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Retrieve an auth token from an OpenStack Cloud
requirements:

View File

@@ -8,7 +8,7 @@ DOCUMENTATION = '''
---
module: baremetal_inspect
short_description: Explicitly triggers baremetal node introspection in ironic.
author: "Julia Kreger (@juliakreger)"
author: OpenStack Ansible SIG
description:
- Requests Ironic to set a node into inspect state in order to collect metadata regarding the node.
This command may be out of band or in-band depending on the ironic driver configuration.
@@ -75,10 +75,14 @@ EXAMPLES = '''
name: "testnode1"
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
def _choose_id_value(module):
@@ -90,31 +94,14 @@ def _choose_id_value(module):
def main():
argument_spec = openstack_full_argument_spec(
auth_type=dict(required=False),
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
mac=dict(required=False),
ironic_url=dict(required=False),
timeout=dict(default=1200, type='int', required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears to be disabled, "
"Please define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
module = IronicModule(argument_spec, **module_kwargs)
sdk, cloud = openstack_cloud_from_module(module)
try:

View File

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

View File

@@ -8,7 +8,7 @@ DOCUMENTATION = '''
---
module: baremetal_node_action
short_description: Activate/Deactivate Bare Metal Resources from OpenStack
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Deploy to nodes controlled by Ironic.
options:
@@ -132,10 +132,15 @@ EXAMPLES = '''
delegate_to: localhost
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.ironic import (
IronicModule,
ironic_argument_spec,
)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_module_kwargs,
openstack_cloud_from_module
)
def _choose_id_value(module):
@@ -227,12 +232,11 @@ def _check_set_power_state(module, cloud, node):
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = ironic_argument_spec(
uuid=dict(required=False),
name=dict(required=False),
instance_info=dict(type='dict', required=False),
config_drive=dict(type='raw', required=False),
ironic_url=dict(required=False),
state=dict(required=False, default='present'),
maintenance=dict(required=False),
maintenance_reason=dict(required=False),
@@ -242,22 +246,7 @@ def main():
timeout=dict(required=False, type='int', default=1800),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
if (
module.params['auth_type'] in [None, 'None']
and module.params['ironic_url'] is None
):
module.fail_json(msg="Authentication appears disabled, Please "
"define an ironic_url parameter")
if (
module.params['ironic_url']
and module.params['auth_type'] in [None, 'None']
):
module.params['auth'] = dict(
endpoint=module.params['ironic_url']
)
module = IronicModule(argument_spec, **module_kwargs)
if (
module.params['config_drive']

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: catalog_service
short_description: Manage OpenStack Identity services
author: "Sam Yaple (@SamYaple)"
author: OpenStack Ansible SIG
description:
- Create, update, or delete OpenStack Identity service. If a service
with the supplied name already exists, it will be updated with the

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: coe_cluster
short_description: Add/Remove COE cluster from OpenStack Cloud
author: "Feilong Wang (@flwang)"
author: OpenStack Ansible SIG
description:
- Add or Remove COE cluster from the OpenStack Container Infra service.
options:

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: coe_cluster_template
short_description: Add/Remove COE cluster template from OpenStack Cloud
author: "Feilong Wang (@flwang)"
author: OpenStack Ansible SIG
description:
- Add or Remove COE cluster template from the OpenStack Container Infra
service.

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: compute_flavor
short_description: Manage OpenStack compute flavors
author: "David Shrewsbury (@Shrews)"
author: OpenStack Ansible SIG
description:
- Add or remove flavors from OpenStack.
options:

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: compute_flavor_info
short_description: Retrieve information about one or more flavors
author: "David Shrewsbury (@Shrews)"
author: OpenStack Ansible SIG
description:
- Retrieve information about available OpenStack instance flavors. By default,
information about ALL flavors are retrieved. Filters can be applied to get

View File

@@ -23,7 +23,7 @@ options:
requirements:
- "python >= 3.6"
- "openstacksdk"
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
'''
EXAMPLES = '''

View File

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

View File

@@ -7,9 +7,7 @@ DOCUMENTATION = '''
---
module: endpoint
short_description: Manage OpenStack Identity service endpoints
author:
- Mohammed Naser (@mnaser)
- Alberto Murillo (@albertomurillo)
author: OpenStack Ansible SIG
description:
- Create, update, or delete OpenStack Identity service endpoints. If a
service with the same combination of I(service), I(interface) and I(region)
@@ -158,7 +156,10 @@ def main():
try:
service = cloud.get_service(service_name_or_id)
if service is None:
if service is None and state == 'absent':
module.exit_json(changed=False)
elif service is None and state == 'present':
module.fail_json(msg='Service %s does not exist' % service_name_or_id)
filters = dict(service_id=service.id, interface=interface)

View File

@@ -6,8 +6,7 @@ DOCUMENTATION = '''
---
module: federation_idp
short_description: manage a federation Identity Provider
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Manage a federation Identity Provider.
options:

View File

@@ -7,8 +7,7 @@ DOCUMENTATION = '''
module: federation_idp_info
short_description: Get the information about the available federation identity
providers
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Fetch a federation identity provider.
options:

View File

@@ -6,8 +6,7 @@ DOCUMENTATION = '''
---
module: federation_mapping
short_description: Manage a federation mapping
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Manage a federation mapping.
options:

View File

@@ -6,8 +6,7 @@ DOCUMENTATION = '''
---
module: federation_mapping_info
short_description: Get the information about the available federation mappings
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Fetch a federation mapping.
options:

View File

@@ -6,7 +6,7 @@
DOCUMENTATION = '''
---
module: floating_ip
author: Davide Guerri (@dguerri) <davide.guerri@hp.com>
author: OpenStack Ansible SIG
short_description: Add/Remove floating IP from an instance
description:
- Add or Remove a floating IP to an instance.
@@ -79,7 +79,7 @@ extends_documentation_fragment:
'''
EXAMPLES = '''
# Assign a floating IP to the fist interface of `cattle001` from an exiting
# Assign a floating IP to the first interface of `cattle001` from an existing
# external network or nova pool. A new floating IP from the first available
# external network is allocated to the project.
- openstack.cloud.floating_ip:

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: group_assignment
short_description: Associate OpenStack Identity users and groups
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Add and remove users from groups
options:

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: host_aggregate
short_description: Manage OpenStack host aggregates
author: "Jakub Jursa (@kuboj)"
author: OpenStack Ansible SIG
description:
- Create, update, or delete OpenStack host aggregates. If a aggregate
with the supplied name already exists, it will be updated with the

View File

@@ -6,9 +6,7 @@ DOCUMENTATION = '''
---
module: identity_domain
short_description: Manage OpenStack Identity Domains
author:
- Monty Taylor (@emonty)
- Haneef Ali (@haneefs)
author: OpenStack Ansible SIG
description:
- Create, update, or delete OpenStack Identity domains. If a domain
with the supplied name already exists, it will be updated with the

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: identity_domain_info
short_description: Retrieve information about one or more OpenStack domains
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
author: OpenStack Ansible SIG
description:
- Retrieve information about a one or more OpenStack domains
- This module was called C(openstack.cloud.identity_domain_facts) before Ansible 2.9, returning C(ansible_facts).

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: identity_group
short_description: Manage OpenStack Identity Groups
author: "Monty Taylor (@emonty), David Shrewsbury (@Shrews)"
author: OpenStack Ansible SIG
description:
- Manage OpenStack Identity Groups. Groups can be created, deleted or
updated. Only the I(description) value can be updated.

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: identity_group_info
short_description: Retrieve info about one or more OpenStack groups
author: "Phillipe Smith (@phsmith)"
author: OpenStack Ansible SIG
description:
- Retrieve info about a one or more OpenStack groups.
options:

View File

@@ -6,9 +6,7 @@ DOCUMENTATION = '''
---
module: identity_role
short_description: Manage OpenStack Identity Roles
author:
- Monty Taylor (@emonty)
- David Shrewsbury (@Shrews)
author: OpenStack Ansible SIG
description:
- Manage OpenStack Identity Roles.
options:

View File

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

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: identity_user
short_description: Manage OpenStack Identity Users
author: David Shrewsbury (@Shrews)
author: OpenStack Ansible SIG
description:
- Manage OpenStack Identity users. Users can be created,
updated or deleted using this module. A user will be updated

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: identity_user_info
short_description: Retrieve information about one or more OpenStack users
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
author: OpenStack Ansible SIG
description:
- Retrieve information about a one or more OpenStack users
- This module was called C(openstack.cloud.identity_user_facts) before Ansible 2.9, returning C(ansible_facts).

View File

@@ -11,7 +11,7 @@ DOCUMENTATION = '''
---
module: image
short_description: Add/Delete images from OpenStack Cloud
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Add or Remove images from the OpenStack Image Repository
options:

View File

@@ -6,7 +6,7 @@
DOCUMENTATION = '''
module: image_info
short_description: Retrieve information about an image within OpenStack.
author: "Davide Agnello (@dagnello)"
author: OpenStack Ansible SIG
description:
- Retrieve information about a image image from OpenStack.
- This module was called C(openstack.cloud.image_facts) before Ansible 2.9, returning C(ansible_facts).
@@ -142,44 +142,31 @@ openstack_image:
type: int
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (openstack_full_argument_spec,
openstack_module_kwargs,
openstack_cloud_from_module)
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class ImageInfoModule(OpenStackModule):
deprecated_names = ('openstack.cloud.os_image_facts', 'openstack.cloud.os_image_info')
argument_spec = dict(
image=dict(type='str', required=False),
properties=dict(type='dict', required=False),
)
def run(self):
if self.params['image']:
image = self.conn.get_image(self.params['image'])
self.exit(changed=False, openstack_image=image)
else:
images = self.conn.search_images(filters=self.params['properties'])
self.exit(changed=False, openstack_image=images)
def main():
argument_spec = openstack_full_argument_spec(
image=dict(required=False),
properties=dict(default=None, type='dict'),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, **module_kwargs)
is_old_facts = module._name == 'openstack.cloud.image_facts'
if is_old_facts:
module.deprecate("The 'openstack.cloud.image_facts' module has been renamed to 'openstack.cloud.image_info', "
"and the renamed one no longer returns ansible_facts", version='2.13')
sdk, cloud = openstack_cloud_from_module(module)
try:
if module.params['image']:
image = cloud.get_image(module.params['image'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=image))
else:
module.exit_json(changed=False, openstack_image=image)
else:
images = cloud.search_images(filters=module.params['properties'])
if is_old_facts:
module.exit_json(changed=False, ansible_facts=dict(
openstack_image=images))
else:
module.exit_json(changed=False, openstack_image=images)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
module = ImageInfoModule()
module()
if __name__ == '__main__':

View File

@@ -9,7 +9,7 @@ DOCUMENTATION = '''
---
module: keypair
short_description: Add/Delete a keypair from OpenStack
author: "Benno Joy (@bennojoy)"
author: OpenStack Ansible SIG
description:
- Add or Remove key pair from OpenStack
options:
@@ -79,23 +79,14 @@ private_key:
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(module, keypair):
state = module.params['state']
if state == 'present' and not keypair:
return True
if state == 'absent' and keypair:
return True
return False
class KeyPairModule(OpenStackModule):
deprecated_names = ('os_keypair', 'openstack.cloud.os_keypair')
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = dict(
name=dict(required=True),
public_key=dict(default=None),
public_key_file=dict(default=None),
@@ -103,58 +94,62 @@ def main():
choices=['absent', 'present', 'replace']),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[['public_key', 'public_key_file']])
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _system_state_change(self, keypair):
state = self.params['state']
if state == 'present' and not keypair:
return True
if state == 'absent' and keypair:
return True
return False
state = module.params['state']
name = module.params['name']
public_key = module.params['public_key']
def run(self):
if module.params['public_key_file']:
with open(module.params['public_key_file']) as public_key_fh:
public_key = public_key_fh.read().rstrip()
state = self.params['state']
name = self.params['name']
public_key = self.params['public_key']
sdk, cloud = openstack_cloud_from_module(module)
try:
keypair = cloud.get_keypair(name)
if self.params['public_key_file']:
with open(self.params['public_key_file']) as public_key_fh:
public_key = public_key_fh.read().rstrip()
if module.check_mode:
module.exit_json(changed=_system_state_change(module, keypair))
keypair = self.conn.get_keypair(name)
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(keypair))
if state in ('present', 'replace'):
if keypair and keypair['name'] == name:
if public_key and (public_key != keypair['public_key']):
if state == 'present':
module.fail_json(
self.fail_json(
msg="Key name %s present but key hash not the same"
" as offered. Delete key first." % name
)
else:
cloud.delete_keypair(name)
keypair = cloud.create_keypair(name, public_key)
self.conn.delete_keypair(name)
keypair = self.conn.create_keypair(name, public_key)
changed = True
else:
changed = False
else:
keypair = cloud.create_keypair(name, public_key)
keypair = self.conn.create_keypair(name, public_key)
changed = True
module.exit_json(changed=changed,
key=keypair,
id=keypair['id'])
self.exit_json(changed=changed, key=keypair, id=keypair['id'])
elif state == 'absent':
if keypair:
cloud.delete_keypair(name)
module.exit_json(changed=True)
module.exit_json(changed=False)
self.conn.delete_keypair(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 = KeyPairModule()
module()
if __name__ == '__main__':

View File

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

View File

@@ -6,8 +6,7 @@ DOCUMENTATION = '''
---
module: keystone_federation_protocol
short_description: manage a federation Protocol
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Manage a federation Protocol.
options:

View File

@@ -6,8 +6,7 @@ DOCUMENTATION = '''
---
module: keystone_federation_protocol_info
short_description: get information about federation Protocols
author:
- "Mark Chappell (@tremble) <mchappel@redhat.com>"
author: OpenStack Ansible SIG
description:
- Get information about federation Protocols.
options:

View File

@@ -0,0 +1,291 @@
#!/usr/bin/python
# Copyright (c) 2020 Jesper Schmitz Mouridsen.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: lb_health_monitor
author: OpenStack Ansible SIG
short_description: Add/Delete a health m nonitor to a pool in the load balancing service from OpenStack Cloud
description:
- Add or Remove a health monitor to/from a pool in the OpenStack load-balancer service.
options:
name:
type: 'str'
description:
- Name that has to be given to the health monitor
required: true
state:
type: 'str'
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
pool:
required: true
type: 'str'
description:
- The pool name or id to monitor by the health monitor.
type:
type: 'str'
default: HTTP
description:
- One of HTTP, HTTPS, PING, SCTP, TCP, TLS-HELLO, or UDP-CONNECT.
choices: [HTTP, HTTPS, PING, SCTP, TCP, TLS-HELLO, UDP-CONNECT]
delay:
type: 'str'
required: true
description:
- the interval, in seconds, between health checks.
max_retries:
required: true
type: 'str'
description:
- The number of successful checks before changing the operating status of the member to ONLINE.
max_retries_down:
type: 'str'
default: 3
description:
- The number of allowed check failures before changing the operating status of the member to ERROR. A valid value is from 1 to 10. The default is 3.
resp_timeout:
required: true
description:
- The time, in seconds, after which a health check times out. Must be less than delay
type: int
admin_state_up:
default: True
description:
- The admin state of the helath monitor true for up or false for down
type: bool
expected_codes:
type: 'str'
default: 200
description:
- The list of HTTP status codes expected in response from the member to declare it healthy. Specify one of the following values
A single value, such as 200
A list, such as 200, 202
A range, such as 200-204
http_method:
type: 'str'
default: GET
choices: ['GET', 'CONNECT', 'DELETE', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']
description:
- The HTTP method that the health monitor uses for requests. One of CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, or TRACE. The default is GET.
url_path:
type: 'str'
default: '/'
description:
- The HTTP URL path of the request sent by the monitor to test the health of a backend member.
Must be a string that begins with a forward slash (/). The default URL path is /.
requirements: ["openstacksdk"]
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
#Create a healtmonitor named healthmonitor01 with method HEAD url_path /status and expect code 200
- openstack.cloud.lb_health_monitor:
auth:
auth_url: "{{keystone_url}}"
username: "{{username}}"
password: "{{password}}"
project_domain_name: "{{domain_name}}"
user_domain_name: "{{domain_name}}"
project_name: "{{project_name}}"
wait: true
admin_state_up: True
expected_codes: '200'
max_retries_down: '4'
http_method: GET
url_path: "/status"
pool: '{{pool_id}}'
name: 'healthmonitor01'
delay: '10'
max_retries: '3'
resp_timeout: '5'
state: present
'''
RETURN = '''
health_monitor:
description: Dictionary describing the health monitor.
returned: On success when C(state=present)
type: complex
contains:
id:
description: The health monitor UUID.
returned: On success when C(state=present)
type: str
admin_state_up:
returned: On success when C(state=present)
description: The administrative state of the resource.
type: bool
created_at:
returned: On success when C(state=present)
description: The UTC date and timestamp when the resource was created.
type: str
delay:
returned: On success when C(state=present)
description: The time, in seconds, between sending probes to members.
type: int
expected_codes:
returned: On success when C(state=present)
description: The list of HTTP status codes expected in response from the member to declare it healthy.
type: str
http_method:
returned: On success when C(state=present)
description: The HTTP method that the health monitor uses for requests.
type: str
max_retries:
returned: On success when C(state=present)
description: The number of successful checks before changing the operating status of the member to ONLINE.
type: str
max_retries_down:
returned: On success when C(state=present)
description: The number of allowed check failures before changing the operating status of the member to ERROR.
type: str
name:
returned: On success when C(state=present)
description: Human-readable name of the resource.
type: str
operating_status:
returned: On success when C(state=present)
description: The operating status of the resource.
type: str
pool_id:
returned: On success when C(state=present)
description: The id of the pool.
type: str
project_id:
returned: On success when C(state=present)
description: The ID of the project owning this resource.
type: str
provisioning_status:
returned: On success when C(state=present)
description: The provisioning status of the resource.
type: str
timeout:
returned: On success when C(state=present)
description: The maximum time, in seconds, that a monitor waits to connect before it times out.
type: int
type:
returned: On success when C(state=present)
description: The type of health monitor.
type: str
updated_at:
returned: On success when C(state=present)
description: The UTC date and timestamp when the resource was last updated.
type: str
url_path:
returned: On success when C(state=present)
description: The HTTP URL path of the request sent by the monitor to test the health of a backend member.
type: str
'''
import time
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class HealthMonitorModule(OpenStackModule):
def _wait_for_health_monitor_status(self, health_monitor_id, status, failures, interval=5):
timeout = self.params['timeout']
total_sleep = 0
if failures is None:
failures = []
while total_sleep < timeout:
health_monitor = self.conn.load_balancer.get_health_monitor(health_monitor_id)
provisioning_status = health_monitor.provisioning_status
if provisioning_status == status:
return health_monitor
if provisioning_status in failures:
self._fail_json(
msg="health monitor %s transitioned to failure state %s" %
(health_monitor, provisioning_status)
)
time.sleep(interval)
total_sleep += interval
self._fail_json(msg="timeout waiting for health monitor %s to transition to %s" %
(health_monitor_id, status)
)
argument_spec = dict(
name=dict(required=True),
delay=dict(required=True),
max_retries=dict(required=True),
max_retries_down=dict(required=False, default="3"),
resp_timeout=dict(required=True, type='int'),
pool=dict(required=True),
expected_codes=dict(required=False, default="200"),
admin_state_up=dict(required=False, default=True, type='bool'),
state=dict(default='present', choices=['absent', 'present']),
http_method=dict(default="GET", requried=False, choices=["GET", "CONNECT", "DELETE",
"HEAD", "OPTIONS", "PATCH",
"POST", "PUT", "TRACE"]),
url_path=dict(default="/", requires=False),
type=dict(default='HTTP',
choices=['HTTP', 'HTTPS', 'PING', 'SCTP', 'TCP', 'TLS-HELLO', 'UDP-CONNECT']))
module_kwargs = dict(supports_check_mode=True)
def run(self):
try:
changed = False
health_monitor = self.conn.load_balancer.find_health_monitor(name_or_id=self.params['name'])
pool = self.conn.load_balancer.find_pool(name_or_id=self.params['pool'])
if self.params['state'] == 'present':
if not health_monitor:
changed = True
health_attrs = {"pool_id": pool.id,
"type": self.params["type"],
"delay": self.params['delay'],
"max_retries": self.params['max_retries'],
"max_retries_down": self.params['max_retries_down'],
"timeout": self.params['resp_timeout'],
"name": self.params['name'],
"admin_state_up": self.params["admin_state_up"],
}
if self.params["type"] in ["HTTP", "HTTPS"]:
health_attrs["expected_codes"] = self.params["expected_codes"]
health_attrs["http_method"] = self.params["http_method"]
health_attrs["url_path"] = self.params["url_path"]
if self.ansible.check_mode:
self.exit_json(changed=True)
health_monitor = self.conn.load_balancer.create_health_monitor(**health_attrs)
if not self.params['wait']:
self.exit_json(changed=changed, id=health_monitor.id,
health_monitor=health_monitor.to_dict())
else:
health_monitor = self._wait_for_health_monitor_status(health_monitor.id, "ACTIVE", ["ERROR"])
self.exit_json(changed=changed, id=health_monitor.id,
health_monitor=health_monitor.to_dict())
else:
self.exit_json(changed=changed, id=health_monitor.id,
health_monitor=health_monitor.to_dict()
)
elif self.params['state'] == 'absent':
if health_monitor:
if self.ansible.check_mode:
self.exit_json(changed=True)
self.conn.load_balancer.delete_health_monitor(health_monitor)
changed = True
self.exit_json(changed=changed)
except Exception as e:
self.fail(msg=str(e))
def main():
module = HealthMonitorModule()
module()
if __name__ == "__main__":
main()

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: lb_listener
short_description: Add/Delete a listener for a load balancer from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
author: OpenStack Ansible SIG
description:
- Add or Remove a listener for a load balancer from the OpenStack load-balancer service.
options:

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: lb_member
short_description: Add/Delete a member for a pool in load balancer from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
author: OpenStack Ansible SIG
description:
- Add or Remove a member for a pool from the OpenStack load-balancer service.
options:

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: lb_pool
short_description: Add/Delete a pool in the load balancing service from OpenStack Cloud
author: "Lingxian Kong (@lingxiankong)"
author: OpenStack Ansible SIG
description:
- Add or Remove a pool from the OpenStack load-balancer service.
options:

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ DOCUMENTATION = '''
---
module: object
short_description: Create or Delete objects and containers from OpenStack
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Create or Delete objects and containers from OpenStack
options:

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: port
short_description: Add/Update/Delete ports from an OpenStack cloud.
author: "Davide Agnello (@dagnello)"
author: OpenStack Ansible SIG
description:
- Add, Update or Remove ports from an OpenStack cloud. A I(state) of
'present' will ensure the port is created or updated if required.
@@ -290,7 +290,7 @@ def _needs_update(module, port, cloud):
return True
for key in compare_list_dict:
if not module.params[key]:
if module.params[key]:
if not port[key]:
return True

View File

@@ -6,7 +6,7 @@
DOCUMENTATION = '''
module: port_info
short_description: Retrieve information about ports within OpenStack.
author: "David Shrewsbury (@Shrews)"
author: OpenStack Ansible SIG
description:
- Retrieve information about ports from OpenStack.
- This module was called C(openstack.cloud.port_facts) before Ansible 2.9, returning C(ansible_facts).

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: project
short_description: Manage OpenStack Projects
author: "Alberto Gireud (@agireud)"
author: OpenStack Ansible SIG
description:
- Manage OpenStack Projects. Projects can be created,
updated or deleted using this module. A project will be updated
@@ -115,7 +115,7 @@ def _needs_update(module, project):
if properties:
project_properties = project.get('properties')
for k, v in properties.items():
if v is not None and v != project_properties[k]:
if v is not None and (k not in project_properties or v != project_properties[k]):
return True
return False

View File

@@ -17,7 +17,7 @@ DOCUMENTATION = '''
---
module: project_access
short_description: Manage OpenStack compute flavors access
author: "Roberto Polli (@ioggstream)"
author: OpenStack Ansible SIG
description:
- Add or remove flavor, volume_type or other resources access
from OpenStack.

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: project_info
short_description: Retrieve information about one or more OpenStack projects
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
author: OpenStack Ansible SIG
description:
- Retrieve information about a one or more OpenStack projects
- This module was called C(openstack.cloud.project_facts) before Ansible 2.9, returning C(ansible_facts).

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: quota
short_description: Manage OpenStack Quotas
author: "Michael Gale (@mgale) <gale.michael@gmail.com>"
author: OpenStack Ansible SIG
description:
- Manage OpenStack Quotas. Quotas can be created,
updated or deleted using this module. A quota will be updated

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: recordset
short_description: Manage OpenStack DNS recordsets
author: "Ricardo Carrillo Cruz (@rcarrillocruz)"
author: OpenStack Ansible SIG
description:
- Manage OpenStack DNS recordsets. Recordsets can be created, deleted or
updated. Only the I(records), I(description), and I(ttl) values
@@ -19,13 +19,14 @@ options:
type: str
name:
description:
- Name of the recordset
- Name of the recordset. It must be ended with name of dns zone.
required: true
type: str
recordset_type:
description:
- Recordset type
- Required when I(state=present).
choices: ['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']
type: str
records:
description:
@@ -61,8 +62,8 @@ EXAMPLES = '''
cloud: mycloud
state: present
zone: example.net.
name: www
recordset_type: primary
name: www.example.net.
recordset_type: "a"
records: ['10.1.1.1']
description: test recordset
ttl: 3600
@@ -72,7 +73,9 @@ EXAMPLES = '''
cloud: mycloud
state: present
zone: example.net.
name: www
name: www.example.net.
recordset_type: "a"
records: ['10.1.1.1']
ttl: 7200
# Delete recordset named "www.example.net."
@@ -80,7 +83,7 @@ EXAMPLES = '''
cloud: mycloud
state: absent
zone: example.net.
name: www
name: www.example.net.
'''
RETURN = '''
@@ -125,7 +128,7 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
openstack_cloud_from_module)
def _system_state_change(state, records, description, ttl, zone, recordset):
def _system_state_change(state, records, description, ttl, recordset):
if state == 'present':
if recordset is None:
return True
@@ -144,10 +147,10 @@ def main():
argument_spec = openstack_full_argument_spec(
zone=dict(required=True),
name=dict(required=True),
recordset_type=dict(required=False),
recordset_type=dict(required=False, choices=['a', 'aaaa', 'mx', 'cname', 'txt', 'ns', 'srv', 'ptr', 'caa']),
records=dict(required=False, type='list', elements='str'),
description=dict(required=False, default=None),
ttl=dict(required=False, default=None, type='int'),
ttl=dict(required=False, type='int'),
state=dict(default='present', choices=['absent', 'present']),
)
@@ -159,76 +162,77 @@ def main():
supports_check_mode=True,
**module_kwargs)
module.module_min_sdk_version = '0.28.0'
zone = module.params.get('zone')
name = module.params.get('name')
state = module.params.get('state')
sdk, cloud = openstack_cloud_from_module(module)
try:
recordset_type = module.params.get('recordset_type')
recordset_filter = {'type': recordset_type}
recordsets = cloud.search_recordsets(zone, name_or_id=name)
recordsets = cloud.search_recordsets(zone, name_or_id=name, filters=recordset_filter)
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
if len(recordsets) == 1:
recordset = recordsets[0]
try:
recordset_id = recordset['id']
except KeyError as e:
module.fail_json(msg=str(e))
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')
kwargs = {}
if description:
kwargs['description'] = description
kwargs['records'] = records
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
records, description,
ttl, recordset))
if recordset is None:
if ttl:
kwargs['ttl'] = ttl
else:
kwargs['ttl'] = 300
recordset = cloud.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
**kwargs)
changed = True
else:
# recordsets is filtered by type and should never be more than 1 return
recordset = None
if state == 'present':
records = module.params.get('records')
description = module.params.get('description')
ttl = module.params.get('ttl')
if ttl:
kwargs['ttl'] = ttl
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
records, description,
ttl, zone,
recordset))
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)
if recordset is None:
recordset = cloud.create_recordset(
zone=zone, name=name, recordset_type=recordset_type,
records=records, description=description, ttl=ttl)
changed = True
else:
if records is None:
records = []
module.exit_json(changed=changed, recordset=recordset)
pre_update_recordset = recordset
changed = _system_state_change(state, records,
description, ttl,
zone, pre_update_recordset)
if changed:
zone = cloud.update_recordset(
zone, recordset_id,
records=records,
description=description,
ttl=ttl)
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
None, None,
None, recordset))
module.exit_json(changed=changed, recordset=recordset)
elif state == 'absent':
if module.check_mode:
module.exit_json(changed=_system_state_change(state,
None, None,
None,
None, recordset))
if recordset is None:
changed = False
else:
cloud.delete_recordset(zone, recordset_id)
changed = True
module.exit_json(changed=changed)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
if recordset is None:
changed = False
else:
cloud.delete_recordset(zone, recordset_id)
changed = True
module.exit_json(changed=changed)
if __name__ == '__main__':

View File

@@ -6,7 +6,7 @@ DOCUMENTATION = '''
---
module: role_assignment
short_description: Associate OpenStack Identity users and roles
author: "Monty Taylor (@emonty), David Shrewsbury (@Shrews)"
author: OpenStack Ansible SIG
description:
- Grant and revoke roles in either project or domain context for
OpenStack Identity Users.

View File

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

View File

@@ -7,8 +7,7 @@ DOCUMENTATION = '''
---
module: routers_info
short_description: Retrieve information about one or more OpenStack routers.
version_added: "2.10"
author: "Bram Verschueren (@bverschueren)"
author: OpenStack Ansible SIG
description:
- Retrieve information about one or more routers from OpenStack.
options:
@@ -145,25 +144,30 @@ openstack_routers:
type: list
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import openstack_full_argument_spec, openstack_cloud_from_module
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
def main():
class RouterInfoModule(OpenStackModule):
argument_spec = openstack_full_argument_spec(
deprecated_names = ('os_routers_info', 'openstack.cloud.os_routers_info')
argument_spec = dict(
name=dict(required=False, default=None),
filters=dict(required=False, type='dict', default=None)
)
module = AnsibleModule(argument_spec)
sdk, cloud = openstack_cloud_from_module(module)
try:
routers = cloud.search_routers(module.params['name'],
module.params['filters'])
def run(self):
kwargs = self.check_versioned(
filters=self.params['filters']
)
if self.params['name']:
kwargs['name_or_id'] = self.params['name']
routers = self.conn.search_routers(**kwargs)
for router in routers:
interfaces_info = []
for port in cloud.list_router_interfaces(router):
for port in self.conn.list_router_interfaces(router):
if port.device_owner != "network:router_gateway":
for ip_spec in port.fixed_ips:
int_info = {
@@ -174,10 +178,12 @@ def main():
interfaces_info.append(int_info)
router['interfaces_info'] = interfaces_info
module.exit_json(changed=False, openstack_routers=routers)
self.exit(changed=False, openstack_routers=routers)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e))
def main():
module = RouterInfoModule()
module()
if __name__ == '__main__':

View File

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

View File

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

View File

@@ -8,9 +8,7 @@ DOCUMENTATION = '''
---
module: security_group_rule
short_description: Add/Delete rule from an existing security group
author:
- "Benno Joy (@bennojoy)"
- "Jeffrey van Pelt (@Thulium-Drake)"
author: OpenStack Ansible SIG
description:
- Add or Remove rule from an existing security group
options:
@@ -21,8 +19,7 @@ options:
type: str
protocol:
description:
- IP protocols ANY TCP UDP ICMP 112 (VRRP) 132 (SCTP)
choices: ['any', 'tcp', 'udp', 'icmp', '112', '132', None]
- IP protocols ANY TCP UDP ICMP and others, also number in range 0-255
type: str
port_range_min:
description:
@@ -181,10 +178,8 @@ security_group_id:
returned: state == present
'''
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 _ports_match(protocol, module_min, module_max, rule_min, rule_max):
@@ -217,21 +212,20 @@ def _ports_match(protocol, module_min, module_max, rule_min, rule_max):
if protocol == 'any':
return True
# Check if the user is supplying -1 or None values for full TPC/UDP port range.
# Check if the user is supplying -1, 1 to 65535 or None values for full TPC/UDP port range.
if protocol in ['tcp', 'udp'] or protocol is None:
if module_min and module_max and int(module_min) == int(module_max) == -1:
module_min = None
module_max = None
if (
(module_min is None and module_max is None)
and (
rule_min and int(rule_min) == 1
and rule_max and int(rule_max) == 65535
)
not module_min and not module_max
or (int(module_min) in [-1, 1]
and int(module_max) in [-1, 65535])
):
# (None, None) == (1, 65535)
return True
if (
not rule_min and not rule_max
or (int(rule_min) in [-1, 1]
and int(rule_max) in [-1, 65535])
):
# (None, None) == (1, 65535) == (-1, -1)
return True
# Sanity check to make sure we don't have type comparison issues.
if module_min:
@@ -246,60 +240,16 @@ def _ports_match(protocol, module_min, module_max, rule_min, rule_max):
return module_min == rule_min and module_max == rule_max
def _find_matching_rule(module, secgroup, remotegroup):
"""
Find a rule in the group that matches the module parameters.
:returns: The matching rule dict, or None if no matches.
"""
protocol = module.params['protocol']
remote_ip_prefix = module.params['remote_ip_prefix']
ethertype = module.params['ethertype']
direction = module.params['direction']
remote_group_id = remotegroup['id']
class SecurityGroupRuleModule(OpenStackModule):
deprecated_names = ('os_security_group_rule', 'openstack.cloud.os_security_group_rule')
for rule in secgroup['security_group_rules']:
if (
protocol == rule['protocol']
and remote_ip_prefix == rule['remote_ip_prefix']
and ethertype == rule['ethertype']
and direction == rule['direction']
and remote_group_id == rule['remote_group_id']
and _ports_match(
protocol,
module.params['port_range_min'],
module.params['port_range_max'],
rule['port_range_min'],
rule['port_range_max'])
):
return rule
return None
def _system_state_change(module, secgroup, remotegroup):
state = module.params['state']
if secgroup:
rule_exists = _find_matching_rule(module, secgroup, remotegroup)
else:
return False
if state == 'present' and not rule_exists:
return True
if state == 'absent' and rule_exists:
return True
return False
def main():
argument_spec = openstack_full_argument_spec(
argument_spec = dict(
security_group=dict(required=True),
# NOTE(Shrews): None is an acceptable protocol value for
# Neutron, but Nova will balk at this.
protocol=dict(default=None,
choices=[None, 'any', 'tcp', 'udp', 'icmp', '112', '132']),
protocol=dict(type='str'),
port_range_min=dict(required=False, type='int'),
port_range_max=dict(required=False, type='int'),
remote_ip_prefix=dict(required=False, default=None),
remote_group=dict(required=False, default=None),
remote_ip_prefix=dict(required=False),
remote_group=dict(required=False),
ethertype=dict(default='IPv4',
choices=['IPv4', 'IPv6']),
direction=dict(default='ingress',
@@ -309,85 +259,122 @@ def main():
project=dict(default=None),
)
module_kwargs = openstack_module_kwargs(
module_kwargs = dict(
mutually_exclusive=[
['remote_ip_prefix', 'remote_group'],
]
)
module = AnsibleModule(argument_spec,
supports_check_mode=True,
**module_kwargs)
def _find_matching_rule(self, secgroup, remotegroup):
"""
Find a rule in the group that matches the module parameters.
:returns: The matching rule dict, or None if no matches.
"""
protocol = self.params['protocol']
remote_ip_prefix = self.params['remote_ip_prefix']
ethertype = self.params['ethertype']
direction = self.params['direction']
remote_group_id = remotegroup['id']
state = module.params['state']
security_group = module.params['security_group']
remote_group = module.params['remote_group']
project = module.params['project']
changed = False
for rule in secgroup['security_group_rules']:
if (
protocol == rule['protocol']
and remote_ip_prefix == rule['remote_ip_prefix']
and ethertype == rule['ethertype']
and direction == rule['direction']
and remote_group_id == rule['remote_group_id']
and _ports_match(
protocol,
self.params['port_range_min'],
self.params['port_range_max'],
rule['port_range_min'],
rule['port_range_max'])
):
return rule
return None
def _system_state_change(self, secgroup, remotegroup):
state = self.params['state']
if secgroup:
rule_exists = self._find_matching_rule(secgroup, remotegroup)
else:
return False
if state == 'present' and not rule_exists:
return True
if state == 'absent' and rule_exists:
return True
return False
def run(self):
state = self.params['state']
security_group = self.params['security_group']
remote_group = self.params['remote_group']
project = self.params['project']
changed = False
sdk, cloud = openstack_cloud_from_module(module)
try:
if project is not None:
proj = cloud.get_project(project)
proj = self.conn.get_project(project)
if proj is None:
module.fail_json(msg='Project %s could not be found' % project)
self.fail_json(msg='Project %s could not be found' % project)
project_id = proj['id']
else:
project_id = cloud.current_project_id
project_id = self.conn.current_project_id
if project_id:
if project_id and not remote_group:
filters = {'tenant_id': project_id}
else:
filters = None
secgroup = cloud.get_security_group(security_group, filters=filters)
secgroup = self.conn.get_security_group(security_group, filters=filters)
if remote_group:
remotegroup = cloud.get_security_group(remote_group,
filters=filters)
remotegroup = self.conn.get_security_group(remote_group, filters=filters)
else:
remotegroup = {'id': None}
if module.check_mode:
module.exit_json(changed=_system_state_change(module, secgroup, remotegroup))
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(secgroup, remotegroup))
if state == 'present':
if module.params['protocol'] == 'any':
module.params['protocol'] = None
if self.params['protocol'] == 'any':
self.params['protocol'] = None
if not secgroup:
module.fail_json(msg='Could not find security group %s' %
security_group)
self.fail_json(msg='Could not find security group %s' % security_group)
rule = _find_matching_rule(module, secgroup, remotegroup)
rule = self._find_matching_rule(secgroup, remotegroup)
if not rule:
kwargs = {}
if project_id:
kwargs['project_id'] = project_id
rule = cloud.create_security_group_rule(
rule = self.conn.create_security_group_rule(
secgroup['id'],
port_range_min=module.params['port_range_min'],
port_range_max=module.params['port_range_max'],
protocol=module.params['protocol'],
remote_ip_prefix=module.params['remote_ip_prefix'],
port_range_min=self.params['port_range_min'],
port_range_max=self.params['port_range_max'],
protocol=self.params['protocol'],
remote_ip_prefix=self.params['remote_ip_prefix'],
remote_group_id=remotegroup['id'],
direction=module.params['direction'],
ethertype=module.params['ethertype'],
direction=self.params['direction'],
ethertype=self.params['ethertype'],
**kwargs
)
changed = True
module.exit_json(changed=changed, rule=rule, id=rule['id'])
self.exit_json(changed=changed, rule=rule, id=rule['id'])
if state == 'absent' and secgroup:
rule = _find_matching_rule(module, secgroup, remotegroup)
rule = self._find_matching_rule(secgroup, remotegroup)
if rule:
cloud.delete_security_group_rule(rule['id'])
self.conn.delete_security_group_rule(rule['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 = SecurityGroupRuleModule()
module()
if __name__ == '__main__':

View File

@@ -0,0 +1,250 @@
#!/usr/bin/python
# coding: utf-8 -*-
#
# Copyright (c) 2020 by Tino Schreiber (Open Telekom Cloud), operated by T-Systems International GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: security_group_rule_info
short_description: Querying security group rules
author: OpenStack Ansible SIG
description:
- Querying security group rules
options:
description:
description:
- Filter the list result by the human-readable description of
the resource.
type: str
direction:
description:
- Filter the security group rule list result by the direction in
which the security group rule is applied.
choices: ['egress', 'ingress']
type: str
ethertype:
description:
- Filter the security group rule list result by the ethertype of
network traffic. The value must be IPv4 or IPv6.
choices: ['IPv4', 'IPv6']
type: str
port_range_min:
description:
- Starting port
type: int
port_range_max:
description:
- Ending port
type: int
project:
description:
- Unique name or ID of the project.
required: false
type: str
protocol:
description:
- Filter the security group rule list result by the IP protocol.
type: str
choices: ['any', 'tcp', 'udp', 'icmp', '112', '132']
remote_group:
description:
- Filter the security group rule list result by the name or ID of the
remote group that associates with this security group rule.
type: str
remote_ip_prefix:
description:
- Source IP address(es) in CIDR notation (exclusive with remote_group)
type: str
revision_number:
description:
- Filter the list result by the revision number of the resource.
type: int
rule:
description:
- Filter the list result by the ID of the security group rule.
type: str
security_group:
description:
- Name or ID of the security group
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
# Get all security group rules
- openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
register: sg
# Filter security group rules for port 80 and name
- openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
security_group: "{{ rule_name }}"
protocol: tcp
port_range_min: 80
port_range_max: 80
remote_ip_prefix: 0.0.0.0/0
# Filter for ICMP rules
- openstack.cloud.security_group_rule_info:
cloud: "{{ cloud }}"
protocol: icmp
'''
RETURN = '''
security_group_rules:
description: List of dictionaries describing security group rules.
type: complex
returned: On Success.
contains:
id:
description: Unique rule UUID.
type: str
description:
description: Human-readable description of the resource.
type: str
sample: 'My description.'
direction:
description: The direction in which the security group rule is applied.
type: str
sample: 'egress'
ethertype:
description: One of IPv4 or IPv6.
type: str
sample: 'IPv4'
port_range_min:
description: The minimum port number in the range that is matched by
the security group rule.
type: int
sample: 8000
port_range_max:
description: The maximum port number in the range that is matched by
the security group rule.
type: int
sample: 8000
project:
description:
- Unique ID of the project.
type: str
sample: '16d53a84a13b49529d2e2c3646691123'
protocol:
description: The protocol that is matched by the security group rule.
type: str
sample: 'tcp'
remote_ip_prefix:
description: The remote IP prefix to be associated with this security group rule.
type: str
sample: '0.0.0.0/0'
security_group_id:
description: The security group ID to associate with this security group rule.
type: str
sample: '729b9660-a20a-41fe-bae6-ed8fa7f69123'
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
OpenStackModule)
class SecurityGroupRuleInfoModule(OpenStackModule):
argument_spec = dict(
description=dict(required=False, type='str'),
direction=dict(required=False,
type='str',
choices=['egress', 'ingress']),
ethertype=dict(required=False,
type='str',
choices=['IPv4', 'IPv6']),
port_range_min=dict(required=False, type='int', min_ver="0.32.0"),
port_range_max=dict(required=False, type='int', min_ver="0.32.0"),
project=dict(required=False, type='str'),
protocol=dict(required=False,
type='str',
choices=['any', 'tcp', 'udp', 'icmp', '112', '132']),
remote_group=dict(required=False, type='str'),
remote_ip_prefix=dict(required=False, type='str', min_ver="0.32.0"),
revision_number=dict(required=False, type='int'),
rule=dict(required=False, type='str'),
security_group=dict(required=False, type='str')
)
module_kwargs = dict(
mutually_exclusive=[
['remote_ip_prefix', 'remote_group'],
]
)
def run(self):
description = self.params['description']
direction = self.params['direction']
ethertype = self.params['ethertype']
project = self.params['project']
protocol = self.params['protocol']
remote_group = self.params['remote_group']
revision_number = self.params['revision_number']
rule = self.params['rule']
security_group = self.params['security_group']
changed = False
filters = self.check_versioned(
port_range_min=self.params['port_range_min'],
port_range_max=self.params['port_range_max'],
remote_ip_prefix=self.params['remote_ip_prefix']
)
data = []
if rule:
sec_rule = self.conn.network.get_security_group_rule(rule)
if sec_rule is None:
self.exit(changed=changed, security_group_rules=[])
self.exit(changed=changed,
security_group_rules=sec_rule.to_dict())
# query parameter id is currently not supported
# PR is open for that.
# filters['id] = sec_rule.id
if description:
filters['description'] = description
if direction:
filters['direction'] = direction
if ethertype:
filters['ethertype'] = ethertype
if project:
proj = self.conn.get_project(project)
if proj is None:
self.fail_json(msg='Project %s could not be found' % project)
filters['project_id'] = proj.id
if protocol:
filters['protocol'] = protocol
if remote_group:
filters['remote_group_id'] = remote_group
if revision_number:
filters['revision_number'] = revision_number
if security_group:
sec_grp = self.conn.network.find_security_group(
name_or_id=security_group,
ignore_missing=True)
if sec_grp is None:
self.fail_json(msg='Security group %s could not be found' % sec_grp)
filters['security_group_id'] = sec_grp.id
for item in self.conn.network.security_group_rules(**filters):
item = item.to_dict()
data.append(item)
self.exit_json(changed=changed,
security_group_rules=data)
def main():
module = SecurityGroupRuleInfoModule()
module()
if __name__ == '__main__':
main()

View File

@@ -11,8 +11,7 @@ DOCUMENTATION = '''
---
module: server
short_description: Create/Delete Compute Instances from OpenStack
version_added: "2.0"
author: "Monty Taylor (@emonty)"
author: OpenStack Ansible SIG
description:
- Create or Remove compute instances from OpenStack.
options:
@@ -162,7 +161,6 @@ options:
scheduler_hints:
description:
- Arbitrary key/value pairs to the scheduler for custom use
version_added: "2.1"
type: dict
state:
description:
@@ -176,7 +174,6 @@ options:
associated with the instance will be deleted along with the instance.
type: bool
default: 'no'
version_added: "2.2"
reuse_ips:
description:
- When I(auto_ip) is true and this option is true, the I(auto_ip) code
@@ -188,11 +185,14 @@ options:
the server is deleted using I(delete_fip).
type: bool
default: 'yes'
version_added: "2.2"
availability_zone:
description:
- Availability zone in which to create the server.
type: str
description:
description:
- Description of the server.
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -468,48 +468,6 @@ def _parse_nics(nics):
yield net
def _network_args(module, cloud):
args = []
nics = module.params['nics']
if not isinstance(nics, list):
module.fail_json(msg='The \'nics\' parameter must be a list.')
for num, net in enumerate(_parse_nics(nics)):
if not isinstance(net, dict):
module.fail_json(
msg='Each entry in the \'nics\' parameter must be a dict.')
if net.get('net-id'):
args.append(net)
elif net.get('net-name'):
by_name = cloud.get_network(net['net-name'])
if not by_name:
module.fail_json(
msg='Could not find network by net-name: %s' %
net['net-name'])
resolved_net = net.copy()
del resolved_net['net-name']
resolved_net['net-id'] = by_name['id']
args.append(resolved_net)
elif net.get('port-id'):
args.append(net)
elif net.get('port-name'):
by_name = cloud.get_port(net['port-name'])
if not by_name:
module.fail_json(
msg='Could not find port by port-name: %s' %
net['port-name'])
resolved_net = net.copy()
del resolved_net['port-name']
resolved_net['port-id'] = by_name['id']
args.append(resolved_net)
if 'tag' in net:
args[num]['tag'] = net['tag']
return args
def _parse_meta(meta):
if isinstance(meta, str):
metas = {}
@@ -522,102 +480,8 @@ def _parse_meta(meta):
return meta
def _detach_ip_list(cloud, server, extra_ips):
for ip in extra_ips:
ip_id = cloud.get_floating_ip(
id=None, filters={'floating_ip_address': ip})
cloud.detach_ip_from_server(
server_id=server.id, floating_ip_id=ip_id)
def _check_ips(module, cloud, server):
changed = False
auto_ip = module.params['auto_ip']
floating_ips = module.params['floating_ips']
floating_ip_pools = module.params['floating_ip_pools']
if floating_ip_pools or floating_ips:
ips = openstack_find_nova_addresses(server.addresses, 'floating')
if not ips:
# If we're configured to have a floating but we don't have one,
# let's add one
server = cloud.add_ips_to_server(
server,
auto_ip=auto_ip,
ips=floating_ips,
ip_pool=floating_ip_pools,
wait=module.params['wait'],
timeout=module.params['timeout'],
)
changed = True
elif floating_ips:
# we were configured to have specific ips, let's make sure we have
# those
missing_ips = []
for ip in floating_ips:
if ip not in ips:
missing_ips.append(ip)
if missing_ips:
server = cloud.add_ip_list(server, missing_ips,
wait=module.params['wait'],
timeout=module.params['timeout'])
changed = True
extra_ips = []
for ip in ips:
if ip not in floating_ips:
extra_ips.append(ip)
if extra_ips:
_detach_ip_list(cloud, server, extra_ips)
changed = True
elif auto_ip:
if server['interface_ip']:
changed = False
else:
# We're configured for auto_ip but we're not showing an
# interface_ip. Maybe someone deleted an IP out from under us.
server = cloud.add_ips_to_server(
server,
auto_ip=auto_ip,
ips=floating_ips,
ip_pool=floating_ip_pools,
wait=module.params['wait'],
timeout=module.params['timeout'],
)
changed = True
return (changed, server)
def _check_security_groups(module, cloud, server):
changed = False
# server security groups were added to shade in 1.19. Until then this
# module simply ignored trying to update security groups and only set them
# on newly created hosts.
if not (
hasattr(cloud, 'add_server_security_groups')
and hasattr(cloud, 'remove_server_security_groups')
):
return changed, server
module_security_groups = set(module.params['security_groups'])
server_security_groups = set(sg['name'] for sg in server.security_groups)
add_sgs = module_security_groups - server_security_groups
remove_sgs = server_security_groups - module_security_groups
if add_sgs:
cloud.add_server_security_groups(server, list(add_sgs))
changed = True
if remove_sgs:
cloud.remove_server_security_groups(server, list(remove_sgs))
changed = True
return (changed, server)
class ServerModule(OpenStackModule):
deprecated_names = ('os_server', 'openstack.cloud.os_server')
argument_spec = dict(
name=dict(required=True),
@@ -645,6 +509,7 @@ class ServerModule(OpenStackModule):
state=dict(default='present', choices=['absent', 'present']),
delete_fip=dict(default=False, type='bool'),
reuse_ips=dict(default=True, type='bool'),
description=dict(default=None, type='str'),
)
module_kwargs = dict(
mutually_exclusive=[
@@ -662,6 +527,7 @@ class ServerModule(OpenStackModule):
)
def run(self):
state = self.params['state']
image = self.params['image']
boot_volume = self.params['boot_volume']
@@ -670,12 +536,12 @@ class ServerModule(OpenStackModule):
if state == 'present':
if not (image or boot_volume):
self.fail_json(
self.fail(
msg="Parameter 'image' or 'boot_volume' is required "
"if state == 'present'"
)
if not flavor and not flavor_ram:
self.fail_json(
self.fail(
msg="Parameter 'flavor' or 'flavor_ram' is required "
"if state == 'present'"
)
@@ -689,7 +555,7 @@ class ServerModule(OpenStackModule):
def _exit_hostvars(self, server, changed=True):
hostvars = self.conn.get_openstack_vars(server)
self.exit_json(
self.exit(
changed=changed, server=server, id=server.id, openstack=hostvars)
def _get_server_state(self):
@@ -697,16 +563,16 @@ class ServerModule(OpenStackModule):
server = self.conn.get_server(self.params['name'])
if server and state == 'present':
if server.status not in ('ACTIVE', 'SHUTOFF', 'PAUSED', 'SUSPENDED'):
self.fail_json(
self.fail(
msg="The instance is available but not Active state: " + server.status)
(ip_changed, server) = _check_ips(self, self.conn, server)
(sg_changed, server) = _check_security_groups(self, self.conn, server)
(ip_changed, server) = self._check_ips(server)
(sg_changed, server) = self._check_security_groups(server)
(server_changed, server) = self._update_server(server)
self._exit_hostvars(server, ip_changed or sg_changed or server_changed)
if server and state == 'absent':
return True
if state == 'absent':
self.exit_json(changed=False, result="not present")
self.exit(changed=False, result="not present")
return True
def _create_server(self):
@@ -719,23 +585,23 @@ class ServerModule(OpenStackModule):
image_id = self.conn.get_image_id(
self.params['image'], self.params['image_exclude'])
if not image_id:
self.fail_json(
self.fail(
msg="Could not find image %s" % self.params['image'])
if flavor:
flavor_dict = self.conn.get_flavor(flavor)
if not flavor_dict:
self.fail_json(msg="Could not find flavor %s" % flavor)
self.fail(msg="Could not find flavor %s" % flavor)
else:
flavor_dict = self.conn.get_flavor_by_ram(flavor_ram, flavor_include)
if not flavor_dict:
self.fail_json(msg="Could not find any matching flavor")
self.fail(msg="Could not find any matching flavor")
nics = _network_args(self, self.conn)
nics = self._network_args()
self.params['meta'] = _parse_meta(self.params['meta'])
bootkwargs = dict(
bootkwargs = self.check_versioned(
name=self.params['name'],
image=image_id,
flavor=flavor_dict['id'],
@@ -747,7 +613,8 @@ class ServerModule(OpenStackModule):
)
for optional_param in (
'key_name', 'availability_zone', 'network',
'scheduler_hints', 'volume_size', 'volumes'):
'scheduler_hints', 'volume_size', 'volumes',
'description'):
if self.params[optional_param]:
bootkwargs[optional_param] = self.params[optional_param]
@@ -770,7 +637,7 @@ class ServerModule(OpenStackModule):
self.params['meta'] = _parse_meta(self.params['meta'])
# cloud.set_server_metadata only updates the key=value pairs, it doesn't
# self.conn.set_server_metadata only updates the key=value pairs, it doesn't
# touch existing ones
update_meta = {}
for (k, v) in self.params['meta'].items():
@@ -792,8 +659,141 @@ class ServerModule(OpenStackModule):
timeout=self.params['timeout'],
delete_ips=self.params['delete_fip'])
except Exception as e:
self.fail_json(msg="Error in deleting vm: %s" % e.message)
self.exit_json(changed=True, result='deleted')
self.fail(msg="Error in deleting vm: %s" % e)
self.exit(changed=True, result='deleted')
def _network_args(self):
args = []
nics = self.params['nics']
if not isinstance(nics, list):
self.fail(msg='The \'nics\' parameter must be a list.')
for num, net in enumerate(_parse_nics(nics)):
if not isinstance(net, dict):
self.fail(
msg='Each entry in the \'nics\' parameter must be a dict.')
if net.get('net-id'):
args.append(net)
elif net.get('net-name'):
by_name = self.conn.get_network(net['net-name'])
if not by_name:
self.fail(
msg='Could not find network by net-name: %s' %
net['net-name'])
resolved_net = net.copy()
del resolved_net['net-name']
resolved_net['net-id'] = by_name['id']
args.append(resolved_net)
elif net.get('port-id'):
args.append(net)
elif net.get('port-name'):
by_name = self.conn.get_port(net['port-name'])
if not by_name:
self.fail(
msg='Could not find port by port-name: %s' %
net['port-name'])
resolved_net = net.copy()
del resolved_net['port-name']
resolved_net['port-id'] = by_name['id']
args.append(resolved_net)
if 'tag' in net:
args[num]['tag'] = net['tag']
return args
def _detach_ip_list(self, server, extra_ips):
for ip in extra_ips:
ip_id = self.conn.get_floating_ip(
id=None, filters={'floating_ip_address': ip})
self.conn.detach_ip_from_server(
server_id=server.id, floating_ip_id=ip_id)
def _check_ips(self, server):
changed = False
auto_ip = self.params['auto_ip']
floating_ips = self.params['floating_ips']
floating_ip_pools = self.params['floating_ip_pools']
if floating_ip_pools or floating_ips:
ips = openstack_find_nova_addresses(server.addresses, 'floating')
if not ips:
# If we're configured to have a floating but we don't have one,
# let's add one
server = self.conn.add_ips_to_server(
server,
auto_ip=auto_ip,
ips=floating_ips,
ip_pool=floating_ip_pools,
wait=self.params['wait'],
timeout=self.params['timeout'],
)
changed = True
elif floating_ips:
# we were configured to have specific ips, let's make sure we have
# those
missing_ips = []
for ip in floating_ips:
if ip not in ips:
missing_ips.append(ip)
if missing_ips:
server = self.conn.add_ip_list(server, missing_ips,
wait=self.params['wait'],
timeout=self.params['timeout'])
changed = True
extra_ips = []
for ip in ips:
if ip not in floating_ips:
extra_ips.append(ip)
if extra_ips:
self._detach_ip_list(server, extra_ips)
changed = True
elif auto_ip:
if server['interface_ip']:
changed = False
else:
# We're configured for auto_ip but we're not showing an
# interface_ip. Maybe someone deleted an IP out from under us.
server = self.conn.add_ips_to_server(
server,
auto_ip=auto_ip,
ips=floating_ips,
ip_pool=floating_ip_pools,
wait=self.params['wait'],
timeout=self.params['timeout'],
)
changed = True
return (changed, server)
def _check_security_groups(self, server):
changed = False
# server security groups were added to shade in 1.19. Until then this
# module simply ignored trying to update security groups and only set them
# on newly created hosts.
if not (
hasattr(self.conn, 'add_server_security_groups')
and hasattr(self.conn, 'remove_server_security_groups')
):
return changed, server
module_security_groups = set(self.params['security_groups'])
server_security_groups = set(sg['name'] for sg in server.security_groups)
add_sgs = module_security_groups - server_security_groups
remove_sgs = server_security_groups - module_security_groups
if add_sgs:
self.conn.add_server_security_groups(server, list(add_sgs))
changed = True
if remove_sgs:
self.conn.remove_server_security_groups(server, list(remove_sgs))
changed = True
return (changed, server)
def main():

View File

@@ -8,40 +8,45 @@ DOCUMENTATION = '''
---
module: server_action
short_description: Perform actions on Compute Instances from OpenStack
author: "Jesse Keating (@omgjlk)"
author: OpenStack Ansible SIG
description:
- Perform server actions on an existing compute instance from OpenStack.
This module does not return any data other than changed true/false.
When I(action) is 'rebuild', then I(image) parameter is required.
- Perform server actions on an existing compute instance from OpenStack.
This module does not return any data other than changed true/false.
When I(action) is 'rebuild', then I(image) parameter is required.
options:
server:
description:
server:
description:
- Name or ID of the instance
required: true
type: str
wait:
description:
required: true
type: str
wait:
description:
- If the module should wait for the instance action to be performed.
type: bool
default: 'yes'
timeout:
description:
type: bool
default: 'yes'
timeout:
description:
- The amount of time the module should wait for the instance to perform
the requested action.
default: 180
type: int
action:
description:
- Perform the given action. The lock and unlock actions always return
changed as the servers API does not provide lock status.
choices: [stop, start, pause, unpause, lock, unlock, suspend, resume,
rebuild]
type: str
required: true
image:
description:
- Image the server should be rebuilt with
type: str
the requested action.
default: 180
type: int
action:
description:
- Perform the given action. The lock and unlock actions always return
changed as the servers API does not provide lock status.
choices: [stop, start, pause, unpause, lock, unlock, suspend, resume,
rebuild, shelve, shelve_offload, unshelve]
type: str
required: true
image:
description:
- Image the server should be rebuilt with
type: str
admin_password:
description:
- Admin password for server to rebuild
type: str
requirements:
- "python >= 3.6"
- "openstacksdk"
@@ -63,177 +68,157 @@ EXAMPLES = '''
timeout: 200
'''
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)
_action_map = {'stop': 'SHUTOFF',
'start': 'ACTIVE',
'pause': 'PAUSED',
'unpause': 'ACTIVE',
'lock': 'ACTIVE', # API doesn't show lock/unlock status
'unlock': 'ACTIVE',
'suspend': 'SUSPENDED',
'resume': 'ACTIVE',
'rebuild': 'ACTIVE'}
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
_admin_actions = ['pause', 'unpause', 'suspend', 'resume', 'lock', 'unlock']
# If I(action) is set to C(shelve) then according to OpenStack's Compute API, the shelved
# server is in one of two possible states:
#
# SHELVED: The server is in shelved state. Depends on the shelve offload time,
# the server will be automatically shelved off loaded.
# SHELVED_OFFLOADED: The shelved server is offloaded (removed from the compute host) and
# it needs unshelved action to be used again.
#
# But wait_for_server can only wait for a single server state. If a shelved server is offloaded
# immediately, then a exceptions.ResourceTimeout will be raised if I(action) is set to C(shelve).
# This is likely to happen because shelved_offload_time in Nova's config is set to 0 by default.
# This also applies if you boot the server from volumes.
#
# Calling C(shelve_offload) instead of C(shelve) will also fail most likely because the default
# policy does not allow C(shelve_offload) for non-admin users while C(shelve) is allowed for
# admin users and server owners.
#
# As we cannot retrieve shelved_offload_time from Nova's config, we fall back to waiting for
# one state and if that fails then we fetch the server's state and match it against the other
# valid states from _action_map.
#
# Ref.: https://docs.openstack.org/api-guide/compute/server_concepts.html
_action_map = {'stop': ['SHUTOFF'],
'start': ['ACTIVE'],
'pause': ['PAUSED'],
'unpause': ['ACTIVE'],
'lock': ['ACTIVE'], # API doesn't show lock/unlock status
'unlock': ['ACTIVE'],
'suspend': ['SUSPENDED'],
'resume': ['ACTIVE'],
'rebuild': ['ACTIVE'],
'shelve': ['SHELVED_OFFLOADED', 'SHELVED'],
'shelve_offload': ['SHELVED_OFFLOADED'],
'unshelve': ['ACTIVE']}
_admin_actions = ['pause', 'unpause', 'suspend', 'resume', 'lock', 'unlock', 'shelve_offload']
def _action_url(server_id):
return '/servers/{server_id}/action'.format(server_id=server_id)
class ServerActionModule(OpenStackModule):
deprecated_names = ('os_server_action', 'openstack.cloud.os_server_action')
argument_spec = dict(
server=dict(required=True, type='str'),
action=dict(required=True, type='str',
choices=['stop', 'start', 'pause', 'unpause',
'lock', 'unlock', 'suspend', 'resume',
'rebuild', 'shelve', 'shelve_offload', 'unshelve']),
image=dict(required=False, type='str'),
admin_password=dict(required=False, type='str'),
)
module_kwargs = dict(
required_if=[('action', 'rebuild', ['image'])],
supports_check_mode=True,
)
def _wait(timeout, cloud, server, action, module, sdk):
"""Wait for the server to reach the desired state for the given action."""
def run(self):
os_server = self._preliminary_checks()
self._execute_server_action(os_server)
# for some reason we don't wait for lock and unlock before exit
if self.params['action'] not in ('lock', 'unlock'):
if self.params['wait']:
self._wait(os_server)
self.exit_json(changed=True)
for count in sdk.utils.iterate_timeout(
timeout,
"Timeout waiting for server to complete %s" % action):
def _preliminary_checks(self):
# Using Munch object for getting information about a server
os_server = self.conn.get_server(self.params['server'])
if not os_server:
self.fail_json(msg='Could not find server %s' % self.params['server'])
# check mode
if self.ansible.check_mode:
self.exit_json(changed=self.__system_state_change(os_server))
# examine special cases
# lock, unlock and rebuild don't depend on state, just do it
if self.params['action'] not in ('lock', 'unlock', 'rebuild'):
if not self.__system_state_change(os_server):
self.exit_json(changed=False)
return os_server
def _execute_server_action(self, os_server):
if self.params['action'] == 'rebuild':
return self._rebuild_server(os_server)
if self.params['action'] == 'shelve_offload':
# shelve_offload is not supported in OpenstackSDK
return self._action(os_server, json={'shelveOffload': None})
action_name = self.params['action'] + "_server"
try:
server = cloud.get_server(server.id)
except Exception:
continue
func_name = getattr(self.conn.compute, action_name)
except AttributeError:
self.fail_json(
msg="Method %s wasn't found in OpenstackSDK compute" % action_name)
func_name(os_server)
if server.status == _action_map[action]:
return
def _rebuild_server(self, os_server):
# rebuild should ensure images exists
try:
image = self.conn.get_image(self.params['image'])
except Exception as e:
self.fail_json(
msg="Can't find the image %s: %s" % (self.params['image'], e))
if not image:
self.fail_json(msg="Image %s was not found!" % self.params['image'])
# admin_password is required by SDK, but not required by Nova API
if self.params['admin_password']:
self.conn.compute.rebuild_server(
server=os_server,
name=os_server['name'],
image=image['id'],
admin_password=self.params['admin_password']
)
else:
self._action(os_server, json={'rebuild': {'imageRef': image['id']}})
if server.status == 'ERROR':
module.fail_json(msg="Server reached ERROR state while attempting to %s" % action)
def _action(self, os_server, json):
response = self.conn.compute.post(
'/servers/{server_id}/action'.format(server_id=os_server['id']),
json=json)
self.sdk.exceptions.raise_from_response(response)
return response
def _wait(self, os_server):
"""Wait for the server to reach the desired state for the given action."""
# Using Server object for wait_for_server function
server = self.conn.compute.find_server(self.params['server'])
states = _action_map[self.params['action']]
def _system_state_change(action, status):
"""Check if system state would change."""
if status == _action_map[action]:
return False
return True
try:
self.conn.compute.wait_for_server(
server,
status=states[0],
wait=self.params['timeout'])
except self.sdk.exceptions.ResourceTimeout:
# raise if there is only one valid state
if len(states) < 2:
raise
# fetch current server status and compare to other valid states
server = self.conn.compute.get_server(os_server['id'])
if server.status not in states:
raise
def __system_state_change(self, os_server):
"""Check if system state would change."""
return os_server.status not in _action_map[self.params['action']]
def main():
argument_spec = openstack_full_argument_spec(
server=dict(required=True),
action=dict(required=True, choices=['stop', 'start', 'pause', 'unpause',
'lock', 'unlock', 'suspend', 'resume',
'rebuild']),
image=dict(required=False),
)
module_kwargs = openstack_module_kwargs()
module = AnsibleModule(argument_spec, supports_check_mode=True,
required_if=[('action', 'rebuild', ['image'])],
**module_kwargs)
action = module.params['action']
wait = module.params['wait']
timeout = module.params['timeout']
image = module.params['image']
sdk, cloud = openstack_cloud_from_module(module)
try:
server = cloud.get_server(module.params['server'])
if not server:
module.fail_json(msg='Could not find server %s' % server)
status = server.status
if module.check_mode:
module.exit_json(changed=_system_state_change(action, status))
if action == 'stop':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'os-stop': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
if action == 'start':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'os-start': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
if action == 'pause':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'pause': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
elif action == 'unpause':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'unpause': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
elif action == 'lock':
# lock doesn't set a state, just do it
cloud.compute.post(
_action_url(server.id),
json={'lock': None})
module.exit_json(changed=True)
elif action == 'unlock':
# unlock doesn't set a state, just do it
cloud.compute.post(
_action_url(server.id),
json={'unlock': None})
module.exit_json(changed=True)
elif action == 'suspend':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'suspend': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
elif action == 'resume':
if not _system_state_change(action, status):
module.exit_json(changed=False)
cloud.compute.post(
_action_url(server.id),
json={'resume': None})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
elif action == 'rebuild':
image = cloud.get_image(image)
if image is None:
module.fail_json(msg="Image does not exist")
# rebuild doesn't set a state, just do it
cloud.compute.post(
_action_url(server.id),
json={'rebuild': {'imageRef': image.id}})
if wait:
_wait(timeout, cloud, server, action, module, sdk)
module.exit_json(changed=True)
except sdk.exceptions.OpenStackCloudException as e:
module.fail_json(msg=str(e), extra_data=e.extra_data)
module = ServerActionModule()
module()
if __name__ == '__main__':

View File

@@ -7,7 +7,7 @@ DOCUMENTATION = '''
---
module: server_group
short_description: Manage OpenStack server groups
author: "Lingxian Kong (@kong)"
author: OpenStack Ansible SIG
description:
- Add or remove server groups from OpenStack.
options:

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