mirror of
https://opendev.org/openstack/ansible-collections-openstack.git
synced 2026-03-27 14:03:03 +00:00
Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
573e219e30 | ||
|
|
5c3750df2c | ||
|
|
94e0e10e49 | ||
|
|
db9a8d5a18 | ||
|
|
7e20a8fc97 | ||
|
|
c7969aa052 | ||
|
|
179a500903 | ||
|
|
ab3e136867 | ||
|
|
8d2391d873 | ||
|
|
e1d62ff73a | ||
|
|
c3261d9813 | ||
|
|
1d8605fbf7 | ||
|
|
0843990dee | ||
|
|
6f8b39bb16 | ||
|
|
c39c8f9d74 | ||
|
|
e4c7bd3df8 | ||
|
|
8a4974025f | ||
|
|
51a9731cef | ||
|
|
051b270d36 | ||
|
|
ca234d7e42 | ||
|
|
5967e1ad0a | ||
|
|
bca980c115 | ||
|
|
a55b817968 | ||
|
|
b7c22515b7 | ||
|
|
70c773fe6d | ||
|
|
a3f809eb99 | ||
|
|
049a2de725 | ||
|
|
69947cd9fd | ||
|
|
c68b33f79f | ||
|
|
62f0da75bb | ||
|
|
6e2bde4060 | ||
|
|
e4a223d160 | ||
|
|
6d4d8d4b0c | ||
|
|
a4e6d1b67c | ||
|
|
3aaf7ff03a | ||
|
|
f6a5433503 | ||
|
|
92c10638a9 | ||
|
|
e3317db56d | ||
|
|
870f82d97b | ||
|
|
87c305907e | ||
|
|
c532560d1b | ||
|
|
ea1f1df805 | ||
|
|
40a32c1e8b | ||
|
|
5881f5423a | ||
|
|
51eba6de02 | ||
|
|
93faf4f1c3 | ||
|
|
c03284abec | ||
|
|
49c95804ba | ||
|
|
c8a5be6b30 | ||
|
|
8255ec4c80 | ||
|
|
36ce09a781 | ||
|
|
0ac75add62 | ||
|
|
d004e0af05 | ||
|
|
b040392238 | ||
|
|
19f24568a2 | ||
|
|
9783fbb972 | ||
|
|
b87e474192 | ||
|
|
c1b8786160 | ||
|
|
981d268039 | ||
|
|
af27a79312 | ||
|
|
e504d807de | ||
|
|
faada98ed9 | ||
|
|
c914c42799 | ||
|
|
d5c403cded | ||
|
|
d36ac1f125 | ||
|
|
15675ce23f | ||
|
|
8180fe8af8 | ||
|
|
88f03fa1df | ||
|
|
565f7fd369 | ||
|
|
47a0d625dc | ||
|
|
b09d8248f7 | ||
|
|
bce3eea5c0 | ||
|
|
134a8e9d23 | ||
|
|
9ed9b1d399 | ||
|
|
80abd782da | ||
|
|
ee9a5c564e | ||
|
|
393b484e5a | ||
|
|
6117f7062e | ||
|
|
f89eea10b4 | ||
|
|
e1178fde34 | ||
|
|
8b35c64fda | ||
|
|
8b98452cbb | ||
|
|
d081bb5378 | ||
|
|
2ce1adad4a | ||
|
|
8ca8df1a84 | ||
|
|
058cb4ff3f | ||
|
|
1c6663999d | ||
|
|
ab96eb6a11 | ||
|
|
38e61994c7 | ||
|
|
d416a27112 | ||
|
|
631e1412a0 | ||
|
|
4c31ea152e | ||
|
|
a39470ac2b | ||
|
|
39a8362d7a | ||
|
|
05da83520e | ||
|
|
a6b52612de | ||
|
|
a67272d1f5 | ||
|
|
f448c78dd4 | ||
|
|
33ce7ab9c7 | ||
|
|
03fadf3b43 | ||
|
|
86a5cc3b42 | ||
|
|
8731fcc64b | ||
|
|
f6a7cf5343 | ||
|
|
284d7871ce | ||
|
|
fcf6fae499 | ||
|
|
88b86be33e | ||
|
|
1f3417cdef | ||
|
|
8792b2b527 | ||
|
|
4a930cf0ec | ||
|
|
e1fe3f6067 | ||
|
|
40ce8103f4 | ||
|
|
bbe1d84448 | ||
|
|
9600baec6e | ||
|
|
abebbe722c | ||
|
|
94e518e42c | ||
|
|
a96d28dfbc | ||
|
|
32ef77d9fd | ||
|
|
4b64ebe623 | ||
|
|
01c2499fb6 | ||
|
|
5e3a91a7c6 | ||
|
|
fc852da4bc | ||
|
|
5717f05102 | ||
|
|
4d0df9f022 | ||
|
|
f8c768ae61 | ||
|
|
4d1017d5d9 | ||
|
|
c75ab0924a | ||
|
|
dfa7983a78 | ||
|
|
8e2e5966b7 | ||
|
|
c9da50e7e7 | ||
|
|
f3610ad0e1 | ||
|
|
7e4fbcf568 | ||
|
|
f89644973d | ||
|
|
b4f015ebd7 | ||
|
|
f0da22da7e | ||
|
|
d206ea000a | ||
|
|
5667600420 | ||
|
|
98ce765383 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
.tox
|
||||
build_artifact
|
||||
ansible_collections
|
||||
galaxy.yml
|
||||
FILES.json
|
||||
MANIFEST.json
|
||||
importer_result.json
|
||||
|
||||
310
.zuul.yaml
310
.zuul.yaml
@@ -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
182
CHANGELOG.rst
Normal 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.
|
||||
32
README.md
32
README.md
@@ -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
151
changelogs/changelog.yaml
Normal 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
31
changelogs/config.yaml
Normal 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
|
||||
0
changelogs/fragments/.keep
Normal file
0
changelogs/fragments/.keep
Normal 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.
|
||||
@@ -1,3 +0,0 @@
|
||||
bugfixes:
|
||||
- Bump the minimum openstacksdk version to 0.29.0 when os_network
|
||||
uses the dns_domain argument.
|
||||
@@ -1,2 +0,0 @@
|
||||
bugfixes:
|
||||
- os_coe_cluster: Retrieve the correct id/uuid depending on whether it is a create/get request.
|
||||
@@ -1,2 +0,0 @@
|
||||
minor_changes:
|
||||
- Moved Openstack inventory script from Ansible community.general to openstack collection.
|
||||
4
ci/roles/dns/defaults/main.yml
Normal file
4
ci/roles/dns/defaults/main.yml
Normal 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']
|
||||
79
ci/roles/dns/tasks/main.yml
Normal file
79
ci/roles/dns/tasks/main.yml
Normal 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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 == []
|
||||
|
||||
2
ci/roles/orchestration/defaults/main.yaml
Normal file
2
ci/roles/orchestration/defaults/main.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
stack_name: "test-stack"
|
||||
11
ci/roles/orchestration/files/hello-world.yaml
Normal file
11
ci/roles/orchestration/files/hello-world.yaml
Normal 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:
|
||||
44
ci/roles/orchestration/tasks/main.yaml
Normal file
44
ci/roles/orchestration/tasks/main.yaml
Normal 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
|
||||
@@ -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 }}"
|
||||
|
||||
@@ -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
|
||||
|
||||
520
ci/roles/server/tasks/server_actions.yml
Normal file
520
ci/roles/server/tasks/server_actions.yml
Normal 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
155
ci/roles/volume/tasks/volume_info.yml
Normal file
155
ci/roles/volume/tasks/volume_info.yml
Normal 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
|
||||
@@ -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}
|
||||
|
||||
@@ -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
5
contrib/generate_module.sh
Executable 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
|
||||
110
contrib/module_info_template.py.j2
Normal file
110
contrib/module_info_template.py.j2
Normal 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()
|
||||
149
contrib/module_template.py.j2
Normal file
149
contrib/module_template.py.j2
Normal 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()
|
||||
81
contrib/module_template_vars.yaml
Normal file
81
contrib/module_template_vars.yaml
Normal 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: []
|
||||
@@ -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
36
galaxy.yml
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
411
meta/runtime.yml
411
meta/runtime.yml
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
68
plugins/module_utils/ironic.py
Normal file
68
plugins/module_utils/ironic.py
Normal 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']
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,7 @@ options:
|
||||
requirements:
|
||||
- "python >= 3.6"
|
||||
- "openstacksdk"
|
||||
author: "Monty Taylor (@emonty)"
|
||||
author: OpenStack Ansible SIG
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
103
plugins/modules/identity_role_info.py
Normal file
103
plugins/modules/identity_role_info.py
Normal 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()
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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__':
|
||||
|
||||
153
plugins/modules/keypair_info.py
Normal file
153
plugins/modules/keypair_info.py
Normal 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()
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
291
plugins/modules/lb_health_monitor.py
Normal file
291
plugins/modules/lb_health_monitor.py
Normal 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()
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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()
|
||||
|
||||
193
plugins/modules/security_group_info.py
Normal file
193
plugins/modules/security_group_info.py
Normal 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()
|
||||
@@ -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__':
|
||||
|
||||
250
plugins/modules/security_group_rule_info.py
Normal file
250
plugins/modules/security_group_rule_info.py
Normal 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()
|
||||
@@ -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():
|
||||
|
||||
@@ -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__':
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user